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  }