gopkg.in/goose.v2@v2.0.1/testservices/hook/service_gccgo.go (about)

     1  // +build gccgo
     2  
     3  package hook
     4  
     5  import (
     6  	"runtime"
     7  	"strings"
     8  )
     9  
    10  // callerDepth defines the number of stack frames to skip during
    11  // currentServiceMethodName. This value differs for various gccgo
    12  // versions.
    13  var callerDepth int
    14  
    15  // namePartsPos defines the position within the raw method name, deliniated by periods.
    16  var namePartsPos = -1 // will panic if we cannot determine the position.
    17  
    18  type inner struct{}
    19  
    20  func (i *inner) m() {
    21  	for callerDepth = 1; ; callerDepth++ {
    22  		pc, _, _, ok := runtime.Caller(callerDepth)
    23  		if !ok {
    24  			panic("current method name cannot be found")
    25  		}
    26  		if name := runtime.FuncForPC(pc).Name(); name == "hook.setCallerDepth" {
    27  			for i, s := range strings.Split(name, ".") {
    28  				if s == "setCallerDepth" {
    29  					namePartsPos = i
    30  					break
    31  				}
    32  			}
    33  			return
    34  		}
    35  	}
    36  }
    37  
    38  type outer struct {
    39  	inner
    40  }
    41  
    42  func setCallerDepth0() {
    43  	var o outer
    44  	o.m()
    45  }
    46  
    47  func setCallerDepth() {
    48  	setCallerDepth0()
    49  }
    50  
    51  func init() {
    52  	setCallerDepth()
    53  	println(callerDepth)
    54  }
    55  
    56  // currentServiceMethodName returns the method executing on the service when ProcessControlHook was invoked.
    57  func (s *TestService) currentServiceMethodName() string {
    58  	// We have to go deeper into the stack with gccgo because in a situation like:
    59  	// type Inner { }
    60  	// func (i *Inner) meth {}
    61  	// type Outer { Inner }
    62  	// o = &Outer{}
    63  	// o.meth()
    64  	// gccgo generates a method called "meth" on *Outer, and this shows up
    65  	// on the stack as seen by runtime.Caller (this might be a gccgo bug).
    66  	pc, _, _, ok := runtime.Caller(callerDepth)
    67  	if !ok {
    68  		panic("current method name cannot be found")
    69  	}
    70  	return unqualifiedMethodName(pc)
    71  }
    72  
    73  func unqualifiedMethodName(pc uintptr) string {
    74  	f := runtime.FuncForPC(pc)
    75  	fullName := f.Name()
    76  	// This is very fragile.  fullName will be something like:
    77  	// launchpad.net_goose_testservices_novaservice.removeServer.pN49_launchpad.net_goose_testservices_novaservice.Nova
    78  	// so if the number of dots in the full package path changes,
    79  	// We try to figure sniff this value at the top, but it may not work.
    80  	nameParts := strings.Split(fullName, ".")
    81  	return nameParts[namePartsPos]
    82  }