github.com/consensys/gnark@v0.11.0/debug/symbol_table.go (about)

     1  package debug
     2  
     3  import (
     4  	"path/filepath"
     5  	"runtime"
     6  	"strings"
     7  )
     8  
     9  type SymbolTable struct {
    10  	Locations  []Location
    11  	Functions  []Function
    12  	mFunctions map[string]int `cbor:"-"` // frame.File+frame.Function to id in Functions
    13  	mLocations map[uint64]int `cbor:"-"` // frame PC to location id in Locations
    14  }
    15  
    16  func NewSymbolTable() SymbolTable {
    17  	return SymbolTable{
    18  		mFunctions: map[string]int{},
    19  		mLocations: map[uint64]int{},
    20  	}
    21  }
    22  
    23  func (st *SymbolTable) CollectStack() []int {
    24  	var r []int
    25  	if Debug {
    26  		r = make([]int, 0, 2)
    27  	} else {
    28  		r = make([]int, 0, 5)
    29  	}
    30  	// derived from: https://golang.org/pkg/runtime/#example_Frames
    31  	// we stop when func name == Define as it is where the gnark circuit code should start
    32  
    33  	// Ask runtime.Callers for up to 10 pcs
    34  	var pc [20]uintptr
    35  	n := runtime.Callers(4, pc[:])
    36  	if n == 0 {
    37  		// No pcs available. Stop now.
    38  		// This can happen if the first argument to runtime.Callers is large.
    39  		return r
    40  	}
    41  	frames := runtime.CallersFrames(pc[:n]) // pass only valid pcs to runtime.CallersFrames
    42  	cpt := 0
    43  	// Loop to get frames.
    44  	// A fixed number of pcs can expand to an indefinite number of Frames.
    45  	for {
    46  		frame, more := frames.Next()
    47  		fe := strings.Split(frame.Function, "/")
    48  		function := fe[len(fe)-1]
    49  
    50  		if !Debug {
    51  			if cpt == 2 {
    52  				// limit stack size to 2 when debug is not set.
    53  				break
    54  			}
    55  			if strings.Contains(function, "runtime.gopanic") {
    56  				continue
    57  			}
    58  			if strings.Contains(function, "frontend.(*constraintSystem)") {
    59  				continue
    60  			}
    61  			if strings.Contains(frame.File, "test/engine.go") {
    62  				continue
    63  			}
    64  			if strings.Contains(frame.File, "gnark/frontend/cs") {
    65  				continue
    66  			}
    67  			frame.File = filepath.Base(frame.File)
    68  		}
    69  
    70  		r = append(r, st.locationID(&frame))
    71  		cpt++
    72  
    73  		if !more {
    74  			break
    75  		}
    76  		if strings.HasSuffix(function, "Define") {
    77  			break
    78  		}
    79  		if strings.HasSuffix(function, "callDeferred") {
    80  			break
    81  		}
    82  	}
    83  	return r
    84  }
    85  
    86  func (st *SymbolTable) locationID(frame *runtime.Frame) int {
    87  	lID, ok := st.mLocations[uint64(frame.PC)]
    88  	if !ok {
    89  		// first let's see if we have the function.
    90  		fID, ok := st.mFunctions[frame.File+frame.Function]
    91  		if !ok {
    92  			fe := strings.Split(frame.Function, "/")
    93  			fName := fe[len(fe)-1]
    94  			f := Function{
    95  				Name:       fName,
    96  				SystemName: frame.Function,
    97  				Filename:   frame.File,
    98  			}
    99  
   100  			st.Functions = append(st.Functions, f)
   101  			fID = len(st.Functions) - 1
   102  			st.mFunctions[frame.File+frame.Function] = fID
   103  		}
   104  
   105  		l := Location{FunctionID: fID, Line: int64(frame.Line)}
   106  
   107  		st.Locations = append(st.Locations, l)
   108  		lID = len(st.Locations) - 1
   109  		st.mLocations[uint64(frame.PC)] = lID
   110  	}
   111  
   112  	return lID
   113  }