Source file src/cmd/cgo/internal/test/callback_windows.go

     1  // Copyright 2023 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package cgotest
     6  
     7  /*
     8  #include <windows.h>
     9  USHORT backtrace(ULONG FramesToCapture, PVOID *BackTrace) {
    10  #ifdef _AMD64_
    11  	CONTEXT context;
    12  	RtlCaptureContext(&context);
    13  	ULONG64 ControlPc;
    14  	ControlPc = context.Rip;
    15  	int i;
    16  	for (i = 0; i < FramesToCapture; i++) {
    17  		PRUNTIME_FUNCTION FunctionEntry;
    18  		ULONG64 ImageBase;
    19  		VOID *HandlerData;
    20  		ULONG64 EstablisherFrame;
    21  
    22  		FunctionEntry = RtlLookupFunctionEntry(ControlPc, &ImageBase, NULL);
    23  
    24  		if (!FunctionEntry) {
    25  			// For simplicity, don't unwind leaf entries, which are not used in this test.
    26  			break;
    27  		} else {
    28  			RtlVirtualUnwind(0, ImageBase, ControlPc, FunctionEntry, &context, &HandlerData, &EstablisherFrame, NULL);
    29  		}
    30  
    31  		ControlPc = context.Rip;
    32  		// Check if we left the user range.
    33  		if (ControlPc < 0x10000) {
    34  			break;
    35  		}
    36  
    37  		BackTrace[i] = (PVOID)(ControlPc);
    38  	}
    39  	return i;
    40  #else
    41  	return 0;
    42  #endif
    43  }
    44  */
    45  import "C"
    46  
    47  import (
    48  	"internal/testenv"
    49  	"reflect"
    50  	"runtime"
    51  	"strings"
    52  	"testing"
    53  	"unsafe"
    54  )
    55  
    56  // Test that the stack can be unwound through a call out and call back
    57  // into Go.
    58  func testCallbackCallersSEH(t *testing.T) {
    59  	testenv.SkipIfOptimizationOff(t) // This test requires inlining.
    60  	if runtime.Compiler != "gc" {
    61  		// The exact function names are not going to be the same.
    62  		t.Skip("skipping for non-gc toolchain")
    63  	}
    64  	if runtime.GOARCH != "amd64" {
    65  		// TODO: support SEH on other architectures.
    66  		t.Skip("skipping on non-amd64")
    67  	}
    68  	// Only frames in the test package are checked.
    69  	want := []string{
    70  		"test._Cfunc_backtrace",
    71  		"test.testCallbackCallersSEH.func1.1",
    72  		"test.testCallbackCallersSEH.func1",
    73  		"test.goCallback",
    74  		"test._Cfunc_callback",
    75  		"test.nestedCall.func1",
    76  		"test.nestedCall",
    77  		"test.testCallbackCallersSEH",
    78  		"test.TestCallbackCallersSEH",
    79  	}
    80  	pc := make([]uintptr, 100)
    81  	n := 0
    82  	nestedCall(func() {
    83  		n = int(C.backtrace(C.DWORD(len(pc)), (*C.PVOID)(unsafe.Pointer(&pc[0]))))
    84  	})
    85  	got := make([]string, 0, n)
    86  	for i := 0; i < n; i++ {
    87  		f := runtime.FuncForPC(pc[i] - 1)
    88  		if f == nil {
    89  			continue
    90  		}
    91  		fname := f.Name()
    92  		switch fname {
    93  		case "goCallback":
    94  			// TODO(qmuntal): investigate why this function doesn't appear
    95  			// when using the external linker.
    96  			continue
    97  		}
    98  		// In module mode, this package has a fully-qualified import path.
    99  		// Remove it if present.
   100  		fname = strings.TrimPrefix(fname, "cmd/cgo/internal/")
   101  		if !strings.HasPrefix(fname, "test.") {
   102  			continue
   103  		}
   104  		got = append(got, fname)
   105  	}
   106  	if !reflect.DeepEqual(want, got) {
   107  		t.Errorf("incorrect backtrace:\nwant:\t%v\ngot:\t%v", want, got)
   108  	}
   109  }
   110  

View as plain text