golang.org/toolchain@v0.0.1-go1.9rc2.windows-amd64/src/cmd/vendor/github.com/google/pprof/internal/driver/driver.go (about)

     1  // Copyright 2014 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 implements the core pprof functionality. It can be
    16  // parameterized with a flag implementation, fetch and symbolize
    17  // mechanisms.
    18  package driver
    19  
    20  import (
    21  	"bytes"
    22  	"fmt"
    23  	"os"
    24  	"path/filepath"
    25  	"regexp"
    26  
    27  	"github.com/google/pprof/internal/plugin"
    28  	"github.com/google/pprof/internal/report"
    29  	"github.com/google/pprof/profile"
    30  )
    31  
    32  // PProf acquires a profile, and symbolizes it using a profile
    33  // manager. Then it generates a report formatted according to the
    34  // options selected through the flags package.
    35  func PProf(eo *plugin.Options) error {
    36  	// Remove any temporary files created during pprof processing.
    37  	defer cleanupTempFiles()
    38  
    39  	o := setDefaults(eo)
    40  
    41  	src, cmd, err := parseFlags(o)
    42  	if err != nil {
    43  		return err
    44  	}
    45  
    46  	p, err := fetchProfiles(src, o)
    47  	if err != nil {
    48  		return err
    49  	}
    50  
    51  	if cmd != nil {
    52  		return generateReport(p, cmd, pprofVariables, o)
    53  	}
    54  
    55  	return interactive(p, o)
    56  }
    57  
    58  func generateReport(p *profile.Profile, cmd []string, vars variables, o *plugin.Options) error {
    59  	p = p.Copy() // Prevent modification to the incoming profile.
    60  
    61  	vars = applyCommandOverrides(cmd, vars)
    62  
    63  	// Delay focus after configuring report to get percentages on all samples.
    64  	relative := vars["relative_percentages"].boolValue()
    65  	if relative {
    66  		if err := applyFocus(p, vars, o.UI); err != nil {
    67  			return err
    68  		}
    69  	}
    70  	ropt, err := reportOptions(p, vars)
    71  	if err != nil {
    72  		return err
    73  	}
    74  	c := pprofCommands[cmd[0]]
    75  	if c == nil {
    76  		panic("unexpected nil command")
    77  	}
    78  	ropt.OutputFormat = c.format
    79  	if len(cmd) == 2 {
    80  		s, err := regexp.Compile(cmd[1])
    81  		if err != nil {
    82  			return fmt.Errorf("parsing argument regexp %s: %v", cmd[1], err)
    83  		}
    84  		ropt.Symbol = s
    85  	}
    86  
    87  	rpt := report.New(p, ropt)
    88  	if !relative {
    89  		if err := applyFocus(p, vars, o.UI); err != nil {
    90  			return err
    91  		}
    92  	}
    93  	if err := aggregate(p, vars); err != nil {
    94  		return err
    95  	}
    96  
    97  	// Generate the report.
    98  	dst := new(bytes.Buffer)
    99  	if err := report.Generate(dst, rpt, o.Obj); err != nil {
   100  		return err
   101  	}
   102  	src := dst
   103  
   104  	// If necessary, perform any data post-processing.
   105  	if c.postProcess != nil {
   106  		dst = new(bytes.Buffer)
   107  		if err := c.postProcess(src, dst, o.UI); err != nil {
   108  			return err
   109  		}
   110  		src = dst
   111  	}
   112  
   113  	// If no output is specified, use default visualizer.
   114  	output := vars["output"].value
   115  	if output == "" {
   116  		if c.visualizer != nil {
   117  			return c.visualizer(src, os.Stdout, o.UI)
   118  		}
   119  		_, err := src.WriteTo(os.Stdout)
   120  		return err
   121  	}
   122  
   123  	// Output to specified file.
   124  	o.UI.PrintErr("Generating report in ", output)
   125  	out, err := os.Create(output)
   126  	if err != nil {
   127  		return err
   128  	}
   129  	if _, err := src.WriteTo(out); err != nil {
   130  		out.Close()
   131  		return err
   132  	}
   133  	return out.Close()
   134  }
   135  
   136  func applyCommandOverrides(cmd []string, v variables) variables {
   137  	trim, focus, tagfocus, hide := v["trim"].boolValue(), true, true, true
   138  
   139  	switch cmd[0] {
   140  	case "proto", "raw":
   141  		trim, focus, tagfocus, hide = false, false, false, false
   142  		v.set("addresses", "t")
   143  	case "callgrind", "kcachegrind":
   144  		trim = false
   145  		v.set("addresses", "t")
   146  	case "disasm", "weblist":
   147  		trim = false
   148  		v.set("addressnoinlines", "t")
   149  	case "peek":
   150  		trim, focus, hide = false, false, false
   151  	case "list":
   152  		v.set("nodecount", "0")
   153  		v.set("lines", "t")
   154  	case "text", "top", "topproto":
   155  		if v["nodecount"].intValue() == -1 {
   156  			v.set("nodecount", "0")
   157  		}
   158  	default:
   159  		if v["nodecount"].intValue() == -1 {
   160  			v.set("nodecount", "80")
   161  		}
   162  	}
   163  	if trim == false {
   164  		v.set("nodecount", "0")
   165  		v.set("nodefraction", "0")
   166  		v.set("edgefraction", "0")
   167  	}
   168  	if focus == false {
   169  		v.set("focus", "")
   170  		v.set("ignore", "")
   171  	}
   172  	if tagfocus == false {
   173  		v.set("tagfocus", "")
   174  		v.set("tagignore", "")
   175  	}
   176  	if hide == false {
   177  		v.set("hide", "")
   178  		v.set("show", "")
   179  	}
   180  	return v
   181  }
   182  
   183  func aggregate(prof *profile.Profile, v variables) error {
   184  	var inlines, function, filename, linenumber, address bool
   185  	switch {
   186  	case v["addresses"].boolValue():
   187  		return nil
   188  	case v["lines"].boolValue():
   189  		inlines = true
   190  		function = true
   191  		filename = true
   192  		linenumber = true
   193  	case v["files"].boolValue():
   194  		inlines = true
   195  		filename = true
   196  	case v["functions"].boolValue():
   197  		inlines = true
   198  		function = true
   199  		filename = true
   200  	case v["noinlines"].boolValue():
   201  		function = true
   202  		filename = true
   203  	case v["addressnoinlines"].boolValue():
   204  		function = true
   205  		filename = true
   206  		linenumber = true
   207  		address = true
   208  	case v["functionnameonly"].boolValue():
   209  		inlines = true
   210  		function = true
   211  	default:
   212  		return fmt.Errorf("unexpected granularity")
   213  	}
   214  	return prof.Aggregate(inlines, function, filename, linenumber, address)
   215  }
   216  
   217  func reportOptions(p *profile.Profile, vars variables) (*report.Options, error) {
   218  	si, mean := vars["sample_index"].value, vars["mean"].boolValue()
   219  	value, meanDiv, sample, err := sampleFormat(p, si, mean)
   220  	if err != nil {
   221  		return nil, err
   222  	}
   223  
   224  	stype := sample.Type
   225  	if mean {
   226  		stype = "mean_" + stype
   227  	}
   228  
   229  	if vars["divide_by"].floatValue() == 0 {
   230  		return nil, fmt.Errorf("zero divisor specified")
   231  	}
   232  
   233  	ropt := &report.Options{
   234  		CumSort:             vars["cum"].boolValue(),
   235  		CallTree:            vars["call_tree"].boolValue(),
   236  		DropNegative:        vars["drop_negative"].boolValue(),
   237  		PositivePercentages: vars["positive_percentages"].boolValue(),
   238  
   239  		CompactLabels: vars["compact_labels"].boolValue(),
   240  		Ratio:         1 / vars["divide_by"].floatValue(),
   241  
   242  		NodeCount:    vars["nodecount"].intValue(),
   243  		NodeFraction: vars["nodefraction"].floatValue(),
   244  		EdgeFraction: vars["edgefraction"].floatValue(),
   245  
   246  		SampleValue:       value,
   247  		SampleMeanDivisor: meanDiv,
   248  		SampleType:        stype,
   249  		SampleUnit:        sample.Unit,
   250  
   251  		OutputUnit: vars["unit"].value,
   252  
   253  		SourcePath: vars["source_path"].stringValue(),
   254  	}
   255  
   256  	if len(p.Mapping) > 0 && p.Mapping[0].File != "" {
   257  		ropt.Title = filepath.Base(p.Mapping[0].File)
   258  	}
   259  
   260  	return ropt, nil
   261  }
   262  
   263  type sampleValueFunc func([]int64) int64
   264  
   265  // sampleFormat returns a function to extract values out of a profile.Sample,
   266  // and the type/units of those values.
   267  func sampleFormat(p *profile.Profile, sampleIndex string, mean bool) (value, meanDiv sampleValueFunc, v *profile.ValueType, err error) {
   268  	if len(p.SampleType) == 0 {
   269  		return nil, nil, nil, fmt.Errorf("profile has no samples")
   270  	}
   271  	index, err := p.SampleIndexByName(sampleIndex)
   272  	if err != nil {
   273  		return nil, nil, nil, err
   274  	}
   275  	value = valueExtractor(index)
   276  	if mean {
   277  		meanDiv = valueExtractor(0)
   278  	}
   279  	v = p.SampleType[index]
   280  	return
   281  }
   282  
   283  func valueExtractor(ix int) sampleValueFunc {
   284  	return func(v []int64) int64 {
   285  		return v[ix]
   286  	}
   287  }