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  }