| // Copyright 2016 The Go Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| package main |
| |
| // Test the context argument to SetCgoTraceback. |
| // Use fake context, traceback, and symbolizer functions. |
| |
| /* |
| // Defined in tracebackctxt_c.c. |
| extern void C1(void); |
| extern void C2(void); |
| extern void tcContext(void*); |
| extern void tcContextSimple(void*); |
| extern void tcTraceback(void*); |
| extern void tcSymbolizer(void*); |
| extern int getContextCount(void); |
| extern void TracebackContextPreemptionCallGo(int); |
| */ |
| import "C" |
| |
| import ( |
| "fmt" |
| "runtime" |
| "sync" |
| "unsafe" |
| ) |
| |
| func init() { |
| register("TracebackContext", TracebackContext) |
| register("TracebackContextPreemption", TracebackContextPreemption) |
| } |
| |
| var tracebackOK bool |
| |
| func TracebackContext() { |
| runtime.SetCgoTraceback(0, unsafe.Pointer(C.tcTraceback), unsafe.Pointer(C.tcContext), unsafe.Pointer(C.tcSymbolizer)) |
| C.C1() |
| if got := C.getContextCount(); got != 0 { |
| fmt.Printf("at end contextCount == %d, expected 0\n", got) |
| tracebackOK = false |
| } |
| if tracebackOK { |
| fmt.Println("OK") |
| } |
| } |
| |
| //export G1 |
| func G1() { |
| C.C2() |
| } |
| |
| //export G2 |
| func G2() { |
| pc := make([]uintptr, 32) |
| n := runtime.Callers(0, pc) |
| cf := runtime.CallersFrames(pc[:n]) |
| var frames []runtime.Frame |
| for { |
| frame, more := cf.Next() |
| frames = append(frames, frame) |
| if !more { |
| break |
| } |
| } |
| |
| want := []struct { |
| function string |
| line int |
| }{ |
| {"main.G2", 0}, |
| {"cFunction", 0x10200}, |
| {"cFunction", 0x200}, |
| {"cFunction", 0x10201}, |
| {"cFunction", 0x201}, |
| {"main.G1", 0}, |
| {"cFunction", 0x10100}, |
| {"cFunction", 0x100}, |
| {"main.TracebackContext", 0}, |
| } |
| |
| ok := true |
| i := 0 |
| wantLoop: |
| for _, w := range want { |
| for ; i < len(frames); i++ { |
| if w.function == frames[i].Function { |
| if w.line != 0 && w.line != frames[i].Line { |
| fmt.Printf("found function %s at wrong line %#x (expected %#x)\n", w.function, frames[i].Line, w.line) |
| ok = false |
| } |
| i++ |
| continue wantLoop |
| } |
| } |
| fmt.Printf("did not find function %s in\n", w.function) |
| for _, f := range frames { |
| fmt.Println(f) |
| } |
| ok = false |
| break |
| } |
| tracebackOK = ok |
| if got := C.getContextCount(); got != 2 { |
| fmt.Printf("at bottom contextCount == %d, expected 2\n", got) |
| tracebackOK = false |
| } |
| } |
| |
| // Issue 47441. |
| func TracebackContextPreemption() { |
| runtime.SetCgoTraceback(0, unsafe.Pointer(C.tcTraceback), unsafe.Pointer(C.tcContextSimple), unsafe.Pointer(C.tcSymbolizer)) |
| |
| const funcs = 10 |
| const calls = 1e5 |
| var wg sync.WaitGroup |
| for i := 0; i < funcs; i++ { |
| wg.Add(1) |
| go func(i int) { |
| defer wg.Done() |
| for j := 0; j < calls; j++ { |
| C.TracebackContextPreemptionCallGo(C.int(i*calls + j)) |
| } |
| }(i) |
| } |
| wg.Wait() |
| |
| fmt.Println("OK") |
| } |
| |
| //export TracebackContextPreemptionGoFunction |
| func TracebackContextPreemptionGoFunction(i C.int) { |
| // Do some busy work. |
| fmt.Sprintf("%d\n", i) |
| } |