github.com/tetratelabs/wazero@v1.7.3-0.20240513003603-48f702e154b5/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 PrintSSAToBackendIRLowering = false 32 PrintRegisterAllocated = false 33 PrintFinalizedMachineCode = false 34 PrintMachineCodeHexPerFunction = printMachineCodeHexPerFunctionUnmodified || PrintMachineCodeHexPerFunctionDisassemblable //nolint 35 printMachineCodeHexPerFunctionUnmodified = false 36 // PrintMachineCodeHexPerFunctionDisassemblable prints the machine code while modifying the actual result 37 // to make it disassemblable. This is useful when debugging the final machine code. See the places where this is used for detail. 38 // When this is enabled, functions must not be called. 39 PrintMachineCodeHexPerFunctionDisassemblable = false 40 ) 41 42 // printTarget is the function index to print the machine code. This is used for debugging to print the machine code 43 // of a specific function. 44 const printTarget = -1 45 46 // PrintEnabledIndex returns true if the current function index is the print target. 47 func PrintEnabledIndex(ctx context.Context) bool { 48 if printTarget == -1 { 49 return true 50 } 51 return GetCurrentFunctionIndex(ctx) == printTarget 52 } 53 54 // ----- Validations ----- 55 const ( 56 // SSAValidationEnabled enables the SSA validation. This is disabled by default since the operation is expensive. 57 SSAValidationEnabled = false 58 ) 59 60 // ----- Stack Guard Check ----- 61 const ( 62 // StackGuardCheckEnabled enables the stack guard check to ensure that our stack bounds check works correctly. 63 StackGuardCheckEnabled = false 64 StackGuardCheckGuardPageSize = 8096 65 ) 66 67 // CheckStackGuardPage checks the given stack guard page is not corrupted. 68 func CheckStackGuardPage(s []byte) { 69 for i := 0; i < StackGuardCheckGuardPageSize; i++ { 70 if s[i] != 0 { 71 panic( 72 fmt.Sprintf("BUG: stack guard page is corrupted:\n\tguard_page=%s\n\tstack=%s", 73 hex.EncodeToString(s[:StackGuardCheckGuardPageSize]), 74 hex.EncodeToString(s[StackGuardCheckGuardPageSize:]), 75 )) 76 } 77 } 78 } 79 80 // ----- Deterministic compilation verifier ----- 81 82 const ( 83 // DeterministicCompilationVerifierEnabled enables the deterministic compilation verifier. This is disabled by default 84 // since the operation is expensive. But when in doubt, enable this to make sure the compilation is deterministic. 85 DeterministicCompilationVerifierEnabled = false 86 DeterministicCompilationVerifyingIter = 5 87 ) 88 89 type ( 90 verifierState struct { 91 initialCompilationDone bool 92 maybeRandomizedIndexes []int 93 r *rand.Rand 94 values map[string]string 95 } 96 verifierStateContextKey struct{} 97 currentFunctionNameKey struct{} 98 currentFunctionIndexKey struct{} 99 ) 100 101 // NewDeterministicCompilationVerifierContext creates a new context with the deterministic compilation verifier used per wasm.Module. 102 func NewDeterministicCompilationVerifierContext(ctx context.Context, localFunctions int) context.Context { 103 maybeRandomizedIndexes := make([]int, localFunctions) 104 for i := range maybeRandomizedIndexes { 105 maybeRandomizedIndexes[i] = i 106 } 107 r := rand.New(rand.NewSource(time.Now().UnixNano())) 108 return context.WithValue(ctx, verifierStateContextKey{}, &verifierState{ 109 r: r, maybeRandomizedIndexes: maybeRandomizedIndexes, values: map[string]string{}, 110 }) 111 } 112 113 // DeterministicCompilationVerifierRandomizeIndexes randomizes the indexes for the deterministic compilation verifier. 114 // To get the randomized index, use DeterministicCompilationVerifierGetRandomizedLocalFunctionIndex. 115 func DeterministicCompilationVerifierRandomizeIndexes(ctx context.Context) { 116 state := ctx.Value(verifierStateContextKey{}).(*verifierState) 117 if !state.initialCompilationDone { 118 // If this is the first attempt, we use the index as-is order. 119 state.initialCompilationDone = true 120 return 121 } 122 r := state.r 123 r.Shuffle(len(state.maybeRandomizedIndexes), func(i, j int) { 124 state.maybeRandomizedIndexes[i], state.maybeRandomizedIndexes[j] = state.maybeRandomizedIndexes[j], state.maybeRandomizedIndexes[i] 125 }) 126 } 127 128 // DeterministicCompilationVerifierGetRandomizedLocalFunctionIndex returns the randomized index for the given `index` 129 // which is assigned by DeterministicCompilationVerifierRandomizeIndexes. 130 func DeterministicCompilationVerifierGetRandomizedLocalFunctionIndex(ctx context.Context, index int) int { 131 state := ctx.Value(verifierStateContextKey{}).(*verifierState) 132 ret := state.maybeRandomizedIndexes[index] 133 return ret 134 } 135 136 // VerifyOrSetDeterministicCompilationContextValue verifies that the `newValue` is the same as the previous value for the given `scope` 137 // and the current function name. If the previous value doesn't exist, it sets the value to the given `newValue`. 138 // 139 // If the verification fails, this prints the diff and exits the process. 140 func VerifyOrSetDeterministicCompilationContextValue(ctx context.Context, scope string, newValue string) { 141 fn := ctx.Value(currentFunctionNameKey{}).(string) 142 key := fn + ": " + scope 143 verifierCtx := ctx.Value(verifierStateContextKey{}).(*verifierState) 144 oldValue, ok := verifierCtx.values[key] 145 if !ok { 146 verifierCtx.values[key] = newValue 147 return 148 } 149 if oldValue != newValue { 150 fmt.Printf( 151 `BUG: Deterministic compilation failed for function%s at scope="%s". 152 153 This is mostly due to (but might not be limited to): 154 * Resetting ssa.Builder, backend.Compiler or frontend.Compiler, etc doens't work as expected, and the compilation has been affected by the previous iterations. 155 * Using a map with non-deterministic iteration order. 156 157 ---------- [old] ---------- 158 %s 159 160 ---------- [new] ---------- 161 %s 162 `, 163 fn, scope, oldValue, newValue, 164 ) 165 os.Exit(1) 166 } 167 } 168 169 // nolint 170 const NeedFunctionNameInContext = PrintSSA || 171 PrintOptimizedSSA || 172 PrintSSAToBackendIRLowering || 173 PrintRegisterAllocated || 174 PrintFinalizedMachineCode || 175 PrintMachineCodeHexPerFunction || 176 DeterministicCompilationVerifierEnabled || 177 PerfMapEnabled 178 179 // SetCurrentFunctionName sets the current function name to the given `functionName`. 180 func SetCurrentFunctionName(ctx context.Context, index int, functionName string) context.Context { 181 ctx = context.WithValue(ctx, currentFunctionNameKey{}, functionName) 182 ctx = context.WithValue(ctx, currentFunctionIndexKey{}, index) 183 return ctx 184 } 185 186 // GetCurrentFunctionName returns the current function name. 187 func GetCurrentFunctionName(ctx context.Context) string { 188 ret, _ := ctx.Value(currentFunctionNameKey{}).(string) 189 return ret 190 } 191 192 // GetCurrentFunctionIndex returns the current function index. 193 func GetCurrentFunctionIndex(ctx context.Context) int { 194 ret, _ := ctx.Value(currentFunctionIndexKey{}).(int) 195 return ret 196 }