github.com/bananabytelabs/wazero@v0.0.0-20240105073314-54b22a776da8/internal/engine/wazevo/wazevoapi/debug_options.go (about) 1 package wazevoapi 2 3 import ( 4 "context" 5 "encoding/hex" 6 "fmt" 7 "math/rand" 8 "os" 9 "time" 10 ) 11 12 // These consts are used various places in the wazevo implementations. 13 // Instead of defining them in each file, we define them here so that we can quickly iterate on 14 // debugging without spending "where do we have debug logging?" time. 15 16 // ----- Debug logging ----- 17 // These consts must be disabled by default. Enable them only when debugging. 18 19 const ( 20 FrontEndLoggingEnabled = false 21 SSALoggingEnabled = false 22 RegAllocLoggingEnabled = false 23 ) 24 25 // ----- Output prints ----- 26 // These consts must be disabled by default. Enable them only when debugging. 27 28 const ( 29 PrintSSA = false 30 PrintOptimizedSSA = false 31 PrintBlockLaidOutSSA = false 32 PrintSSAToBackendIRLowering = false 33 PrintRegisterAllocated = false 34 PrintFinalizedMachineCode = false 35 PrintMachineCodeHexPerFunction = printMachineCodeHexPerFunctionUnmodified || PrintMachineCodeHexPerFunctionDisassemblable //nolint 36 printMachineCodeHexPerFunctionUnmodified = false 37 // PrintMachineCodeHexPerFunctionDisassemblable prints the machine code while modifying the actual result 38 // to make it disassemblable. This is useful when debugging the final machine code. See the places where this is used for detail. 39 // When this is enabled, functions must not be called. 40 PrintMachineCodeHexPerFunctionDisassemblable = false 41 ) 42 43 // printTarget is the function index to print the machine code. This is used for debugging to print the machine code 44 // of a specific function. 45 const printTarget = -1 46 47 // PrintEnabledIndex returns true if the current function index is the print target. 48 func PrintEnabledIndex(ctx context.Context) bool { 49 if printTarget == -1 { 50 return true 51 } 52 return GetCurrentFunctionIndex(ctx) == printTarget 53 } 54 55 // ----- Validations ----- 56 const ( 57 // SSAValidationEnabled enables the SSA validation. This is disabled by default since the operation is expensive. 58 SSAValidationEnabled = false 59 ) 60 61 // ----- Stack Guard Check ----- 62 const ( 63 // StackGuardCheckEnabled enables the stack guard check to ensure that our stack bounds check works correctly. 64 StackGuardCheckEnabled = false 65 StackGuardCheckGuardPageSize = 8096 66 ) 67 68 // CheckStackGuardPage checks the given stack guard page is not corrupted. 69 func CheckStackGuardPage(s []byte) { 70 for i := 0; i < StackGuardCheckGuardPageSize; i++ { 71 if s[i] != 0 { 72 panic( 73 fmt.Sprintf("BUG: stack guard page is corrupted:\n\tguard_page=%s\n\tstack=%s", 74 hex.EncodeToString(s[:StackGuardCheckGuardPageSize]), 75 hex.EncodeToString(s[StackGuardCheckGuardPageSize:]), 76 )) 77 } 78 } 79 } 80 81 // ----- Deterministic compilation verifier ----- 82 83 const ( 84 // DeterministicCompilationVerifierEnabled enables the deterministic compilation verifier. This is disabled by default 85 // since the operation is expensive. But when in doubt, enable this to make sure the compilation is deterministic. 86 DeterministicCompilationVerifierEnabled = false 87 DeterministicCompilationVerifyingIter = 5 88 ) 89 90 type ( 91 verifierState struct { 92 initialCompilationDone bool 93 maybeRandomizedIndexes []int 94 r *rand.Rand 95 values map[string]string 96 } 97 verifierStateContextKey struct{} 98 currentFunctionNameKey struct{} 99 currentFunctionIndexKey struct{} 100 ) 101 102 // NewDeterministicCompilationVerifierContext creates a new context with the deterministic compilation verifier used per wasm.Module. 103 func NewDeterministicCompilationVerifierContext(ctx context.Context, localFunctions int) context.Context { 104 maybeRandomizedIndexes := make([]int, localFunctions) 105 for i := range maybeRandomizedIndexes { 106 maybeRandomizedIndexes[i] = i 107 } 108 r := rand.New(rand.NewSource(time.Now().UnixNano())) 109 return context.WithValue(ctx, verifierStateContextKey{}, &verifierState{ 110 r: r, maybeRandomizedIndexes: maybeRandomizedIndexes, values: map[string]string{}, 111 }) 112 } 113 114 // DeterministicCompilationVerifierRandomizeIndexes randomizes the indexes for the deterministic compilation verifier. 115 // To get the randomized index, use DeterministicCompilationVerifierGetRandomizedLocalFunctionIndex. 116 func DeterministicCompilationVerifierRandomizeIndexes(ctx context.Context) { 117 state := ctx.Value(verifierStateContextKey{}).(*verifierState) 118 if !state.initialCompilationDone { 119 // If this is the first attempt, we use the index as-is order. 120 state.initialCompilationDone = true 121 return 122 } 123 r := state.r 124 r.Shuffle(len(state.maybeRandomizedIndexes), func(i, j int) { 125 state.maybeRandomizedIndexes[i], state.maybeRandomizedIndexes[j] = state.maybeRandomizedIndexes[j], state.maybeRandomizedIndexes[i] 126 }) 127 } 128 129 // DeterministicCompilationVerifierGetRandomizedLocalFunctionIndex returns the randomized index for the given `index` 130 // which is assigned by DeterministicCompilationVerifierRandomizeIndexes. 131 func DeterministicCompilationVerifierGetRandomizedLocalFunctionIndex(ctx context.Context, index int) int { 132 state := ctx.Value(verifierStateContextKey{}).(*verifierState) 133 ret := state.maybeRandomizedIndexes[index] 134 return ret 135 } 136 137 // VerifyOrSetDeterministicCompilationContextValue verifies that the `newValue` is the same as the previous value for the given `scope` 138 // and the current function name. If the previous value doesn't exist, it sets the value to the given `newValue`. 139 // 140 // If the verification fails, this prints the diff and exits the process. 141 func VerifyOrSetDeterministicCompilationContextValue(ctx context.Context, scope string, newValue string) { 142 fn := ctx.Value(currentFunctionNameKey{}).(string) 143 key := fn + ": " + scope 144 verifierCtx := ctx.Value(verifierStateContextKey{}).(*verifierState) 145 oldValue, ok := verifierCtx.values[key] 146 if !ok { 147 verifierCtx.values[key] = newValue 148 return 149 } 150 if oldValue != newValue { 151 fmt.Printf( 152 `BUG: Deterministic compilation failed for function%s at scope="%s". 153 154 This is mostly due to (but might not be limited to): 155 * Resetting ssa.Builder, backend.Compiler or frontend.Compiler, etc doens't work as expected, and the compilation has been affected by the previous iterations. 156 * Using a map with non-deterministic iteration order. 157 158 ---------- [old] ---------- 159 %s 160 161 ---------- [new] ---------- 162 %s 163 `, 164 fn, scope, oldValue, newValue, 165 ) 166 os.Exit(1) 167 } 168 } 169 170 // nolint 171 const NeedFunctionNameInContext = PrintSSA || 172 PrintOptimizedSSA || 173 PrintBlockLaidOutSSA || 174 PrintSSAToBackendIRLowering || 175 PrintRegisterAllocated || 176 PrintFinalizedMachineCode || 177 PrintMachineCodeHexPerFunction || 178 DeterministicCompilationVerifierEnabled || 179 PerfMapEnabled 180 181 // SetCurrentFunctionName sets the current function name to the given `functionName`. 182 func SetCurrentFunctionName(ctx context.Context, index int, functionName string) context.Context { 183 ctx = context.WithValue(ctx, currentFunctionNameKey{}, functionName) 184 ctx = context.WithValue(ctx, currentFunctionIndexKey{}, index) 185 return ctx 186 } 187 188 // GetCurrentFunctionName returns the current function name. 189 func GetCurrentFunctionName(ctx context.Context) string { 190 ret, _ := ctx.Value(currentFunctionNameKey{}).(string) 191 return ret 192 } 193 194 // GetCurrentFunctionIndex returns the current function index. 195 func GetCurrentFunctionIndex(ctx context.Context) int { 196 ret, _ := ctx.Value(currentFunctionIndexKey{}).(int) 197 return ret 198 } 199 200 // ----- High Register Pressure ----- 201 202 type highRegisterPressureContextKey struct{} 203 204 // EnableHighRegisterPressure enables the high register pressure mode. 205 func EnableHighRegisterPressure(ctx context.Context) context.Context { 206 ctx = context.WithValue(ctx, highRegisterPressureContextKey{}, true) 207 return ctx 208 } 209 210 // IsHighRegisterPressure returns true if the current compilation is under high register pressure. 211 func IsHighRegisterPressure(ctx context.Context) bool { 212 return ctx.Value(highRegisterPressureContextKey{}) != nil 213 }