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 }