...

Source file src/pkg/cmd/vendor/github.com/google/pprof/internal/binutils/disasm.go

     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 binutils
    16	
    17	import (
    18		"bytes"
    19		"io"
    20		"regexp"
    21		"strconv"
    22	
    23		"github.com/google/pprof/internal/plugin"
    24		"github.com/ianlancetaylor/demangle"
    25	)
    26	
    27	var (
    28		nmOutputRE            = regexp.MustCompile(`^\s*([[:xdigit:]]+)\s+(.)\s+(.*)`)
    29		objdumpAsmOutputRE    = regexp.MustCompile(`^\s*([[:xdigit:]]+):\s+(.*)`)
    30		objdumpOutputFileLine = regexp.MustCompile(`^(.*):([0-9]+)`)
    31		objdumpOutputFunction = regexp.MustCompile(`^(\S.*)\(\):`)
    32	)
    33	
    34	func findSymbols(syms []byte, file string, r *regexp.Regexp, address uint64) ([]*plugin.Sym, error) {
    35		// Collect all symbols from the nm output, grouping names mapped to
    36		// the same address into a single symbol.
    37	
    38		// The symbols to return.
    39		var symbols []*plugin.Sym
    40	
    41		// The current group of symbol names, and the address they are all at.
    42		names, start := []string{}, uint64(0)
    43	
    44		buf := bytes.NewBuffer(syms)
    45	
    46		for {
    47			symAddr, name, err := nextSymbol(buf)
    48			if err == io.EOF {
    49				// Done. If there was an unfinished group, append it.
    50				if len(names) != 0 {
    51					if match := matchSymbol(names, start, symAddr-1, r, address); match != nil {
    52						symbols = append(symbols, &plugin.Sym{Name: match, File: file, Start: start, End: symAddr - 1})
    53					}
    54				}
    55	
    56				// And return the symbols.
    57				return symbols, nil
    58			}
    59	
    60			if err != nil {
    61				// There was some kind of serious error reading nm's output.
    62				return nil, err
    63			}
    64	
    65			// If this symbol is at the same address as the current group, add it to the group.
    66			if symAddr == start {
    67				names = append(names, name)
    68				continue
    69			}
    70	
    71			// Otherwise append the current group to the list of symbols.
    72			if match := matchSymbol(names, start, symAddr-1, r, address); match != nil {
    73				symbols = append(symbols, &plugin.Sym{Name: match, File: file, Start: start, End: symAddr - 1})
    74			}
    75	
    76			// And start a new group.
    77			names, start = []string{name}, symAddr
    78		}
    79	}
    80	
    81	// matchSymbol checks if a symbol is to be selected by checking its
    82	// name to the regexp and optionally its address. It returns the name(s)
    83	// to be used for the matched symbol, or nil if no match
    84	func matchSymbol(names []string, start, end uint64, r *regexp.Regexp, address uint64) []string {
    85		if address != 0 && address >= start && address <= end {
    86			return names
    87		}
    88		for _, name := range names {
    89			if r == nil || r.MatchString(name) {
    90				return []string{name}
    91			}
    92	
    93			// Match all possible demangled versions of the name.
    94			for _, o := range [][]demangle.Option{
    95				{demangle.NoClones},
    96				{demangle.NoParams},
    97				{demangle.NoParams, demangle.NoTemplateParams},
    98			} {
    99				if demangled, err := demangle.ToString(name, o...); err == nil && r.MatchString(demangled) {
   100					return []string{demangled}
   101				}
   102			}
   103		}
   104		return nil
   105	}
   106	
   107	// disassemble parses the output of the objdump command and returns
   108	// the assembly instructions in a slice.
   109	func disassemble(asm []byte) ([]plugin.Inst, error) {
   110		buf := bytes.NewBuffer(asm)
   111		function, file, line := "", "", 0
   112		var assembly []plugin.Inst
   113		for {
   114			input, err := buf.ReadString('\n')
   115			if err != nil {
   116				if err != io.EOF {
   117					return nil, err
   118				}
   119				if input == "" {
   120					break
   121				}
   122			}
   123	
   124			if fields := objdumpAsmOutputRE.FindStringSubmatch(input); len(fields) == 3 {
   125				if address, err := strconv.ParseUint(fields[1], 16, 64); err == nil {
   126					assembly = append(assembly,
   127						plugin.Inst{
   128							Addr:     address,
   129							Text:     fields[2],
   130							Function: function,
   131							File:     file,
   132							Line:     line,
   133						})
   134					continue
   135				}
   136			}
   137			if fields := objdumpOutputFileLine.FindStringSubmatch(input); len(fields) == 3 {
   138				if l, err := strconv.ParseUint(fields[2], 10, 32); err == nil {
   139					file, line = fields[1], int(l)
   140				}
   141				continue
   142			}
   143			if fields := objdumpOutputFunction.FindStringSubmatch(input); len(fields) == 2 {
   144				function = fields[1]
   145				continue
   146			}
   147			// Reset on unrecognized lines.
   148			function, file, line = "", "", 0
   149		}
   150	
   151		return assembly, nil
   152	}
   153	
   154	// nextSymbol parses the nm output to find the next symbol listed.
   155	// Skips over any output it cannot recognize.
   156	func nextSymbol(buf *bytes.Buffer) (uint64, string, error) {
   157		for {
   158			line, err := buf.ReadString('\n')
   159			if err != nil {
   160				if err != io.EOF || line == "" {
   161					return 0, "", err
   162				}
   163			}
   164	
   165			if fields := nmOutputRE.FindStringSubmatch(line); len(fields) == 4 {
   166				if address, err := strconv.ParseUint(fields[1], 16, 64); err == nil {
   167					return address, fields[3], nil
   168				}
   169			}
   170		}
   171	}
   172	

View as plain text