func TestActiveClose(t *testing.T) { go func() { log.Println(http.ListenAndServe("localhost:6060", nil)) }() s := New() s.RegCb(&testcb{s}) p := make([]runtime.StackRecord, 100) fn, _ := runtime.GoroutineProfile(p) Println(p[:fn]) Println("runtime.NumGoroutine()", runtime.NumGoroutine()) connid, err := s.ConnectBlock("10.1.9.34:9889", time.Second*10) if err != nil { return } time.Sleep(time.Second * 50) fn, _ = runtime.GoroutineProfile(p) Println(p[:fn]) Println("runtime.NumGoroutine()", runtime.NumGoroutine()) s.CloseConn(connid) time.Sleep(time.Second * 50) fn, _ = runtime.GoroutineProfile(p) Println(p[:fn]) Println("runtime.NumGoroutine()", runtime.NumGoroutine()) }
// NewStackTrace is a constructor function which queries the Go runtime // for goroutine information and builds a StackTrace object for each. // A slice of StackTraces, one for each running goroutine, is returned. func NewStackTrace() []*StackTrace { stackRecords := make([]runtime.StackRecord, STACK_BUFFERS) count, ok := runtime.GoroutineProfile(stackRecords) if !ok { return nil } results := make([]*StackTrace, 0) for i := 0; i < count; i++ { st := new(StackTrace) st.Id = i st.Frames = make([]*StackFrame, 0) frames := stackRecords[i].Stack() for x := range frames { f := runtime.FuncForPC(frames[x]) file, line := f.FileLine(frames[x]) sf := new(StackFrame) sf.Name = strings.TrimSpace(filepath.Base(f.Name())) sf.File = strings.TrimSpace(file) sf.Line = line st.Frames = append(st.Frames, sf) } results = append(results, st) } return results }
func TestTimeoutAndMemory(t *testing.T) { dir := "." + string(os.PathSeparator) + "testin" files, err := ioutil.ReadDir(dir) if err != nil { t.Fatal(err) } testHTML := make([]string, 0, len(files)) names := make([]string, 0, len(files)) for _, file := range files { fn := file.Name() if strings.HasSuffix(fn, ".html") { ffn := dir + string(os.PathSeparator) + fn dat, err := ioutil.ReadFile(ffn) if err != nil { t.Fatal(err) } testHTML = append(testHTML, string(dat)) names = append(names, fn) } } fmt.Println("NOTE: this test may take a few minutes to run") var ms runtime.MemStats var alloc1 uint64 goroutineCount1 := 2 // the number of goroutines in a quiet state, more if test flags are used for i := 0; i < 2; i++ { testToMem(testHTML, names, t) limit := 60 var goroutineCount, secs int for secs = 1; secs <= limit; secs++ { time.Sleep(time.Second) // wait for the timeout goroutines to finish goroutineCount, _ = runtime.GoroutineProfile(nil) if goroutineCount == goroutineCount1 { goto correctGoroutines } } t.Error(fmt.Sprintln("after ", secs, "seconds, num goroutines", goroutineCount, "when should be", goroutineCount1)) correctGoroutines: runtime.GC() runtime.ReadMemStats(&ms) if alloc1 == 0 { alloc1 = ms.Alloc // this is set here to allow for static data set-up by 1st pass through fmt.Println("NOTE: base case established in", secs, "seconds. Memory used=", alloc1) } else { increase := (100.0 * float64(ms.Alloc) / float64(alloc1)) - 100.0 if increase > 0.2 { // % t.Error(fmt.Sprintln("run", i, "memory usage changed from", alloc1, "to", ms.Alloc, "increase:", ms.Alloc-alloc1, "which is", increase, "%")) } } } }
func FprintGoroutines(w io.Writer, allFrame bool) { ng := runtime.NumGoroutine() p := make([]runtime.StackRecord, ng) n, ok := runtime.GoroutineProfile(p) if !ok { panic("The slice is too small too put all records") return } for i := 0; i < n; i++ { printStackRecord(w, i, p[i].Stack(), allFrame) } }
func main() { runtime.GOMAXPROCS(200) for i := 0; i < 10; i++ { go func() { for { syscall.Close(-1) } }() } stk := make([]runtime.StackRecord, 1000) for n := 0; ; n++ { _, ok := runtime.GoroutineProfile(stk) if !ok { panic("GoroutineProfile refused") } if n&(n-1) == 0 { println(n) } } }
func TestGoroutineProfile(t *testing.T) { // GoroutineProfile used to use the wrong starting sp for // goroutines coming out of system calls, causing possible // crashes. defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(100)) var stop uint32 defer atomic.StoreUint32(&stop, 1) // in case of panic var wg sync.WaitGroup for i := 0; i < 4; i++ { wg.Add(1) go func() { for atomic.LoadUint32(&stop) == 0 { syscall.Close(-1) } wg.Done() }() } max := 10000 if testing.Short() { max = 100 } stk := make([]runtime.StackRecord, 128) for n := 0; n < max; n++ { _, ok := runtime.GoroutineProfile(stk) if !ok { t.Fatalf("GoroutineProfile failed") } } // If the program didn't crash, we passed. atomic.StoreUint32(&stop, 1) wg.Wait() }
func main() { // arguments if len(os.Args) < 2 { fmt.Printf("usage: %s [server-listen_address] [local-server_address-socks_address]\n", os.Args[0]) return } var serverSessionManagers []*SessionManager var localSessionManager *SessionManager serverDeliveries := make(map[int64]*Delivery) for _, arg := range os.Args[1:] { if strings.HasPrefix(arg, "server") { // start server parts := strings.Split(arg, "-") listener, err := NewDeliveryListener(parts[1]) if err != nil { log.Fatal(err) } listener.OnSignal("notify:delivery", func(delivery *Delivery) { serverDeliveries[delivery.Source] = delivery delivery.CloseCallbacks = append(delivery.CloseCallbacks, func() { delete(serverDeliveries, delivery.Source) }) m := NewSessionManager(delivery) serverSessionManagers = append(serverSessionManagers, m) }) } else if strings.HasPrefix(arg, "local") { // start local parts := strings.Split(arg, "-") delivery, err := NewOutgoingDelivery(parts[1]) if err != nil { log.Fatal(err) } localSessionManager = NewSessionManager(delivery) socksServer, err := NewSocksServer(parts[2]) if err != nil { log.Fatal(err) } socksServer.OnSignal("client", func(conn net.Conn, hostPort string) { session, err := NewOutgoingSession(delivery, hostPort, conn) if err != nil { log.Fatal(err) } localSessionManager.Sessions[session.id] = session session.OnClose(func() { delete(localSessionManager.Sessions, session.id) }) }) } else if strings.HasPrefix(arg, "status") { // status parts := strings.Split(arg, "-") go func() { err := http.ListenAndServe(parts[1], nil) if err != nil { log.Fatal(err) } }() // web status http.HandleFunc("/status", func(w http.ResponseWriter, r *http.Request) { p := func(format string, args ...interface{}) { fmt.Fprintf(w, format, args...) } // status var memStats runtime.MemStats runtime.ReadMemStats(&memStats) p("%d goroutines, %fm in use\n", runtime.NumGoroutine(), float64(memStats.Alloc)/1000000) p("load") for _, delivery := range serverDeliveries { p(" %f", delivery.Load) } p("\n") p("\n") // local sessions if localSessionManager != nil { p("%d local sessions\n", len(localSessionManager.Sessions)) for _, session := range localSessionManager.Sessions { p("%v %v %s\n", session.localClosed, session.remoteClosed, session.hostPort) } p("\n") } // server sessions for _, manager := range serverSessionManagers { p("%d server sessions\n", len(manager.Sessions)) for _, session := range manager.Sessions { p("%v %v %s\n", session.localClosed, session.remoteClosed, session.hostPort) } p("\n") } // goroutines p("stack traces\n") records := make([]runtime.StackRecord, runtime.NumGoroutine()*2) n, _ := runtime.GoroutineProfile(records) stats := make(map[string]int) for i := 0; i < n; i++ { stack := records[i].Stack() entries := make([]string, 0) for _, pc := range stack { f := runtime.FuncForPC(pc) file, line := f.FileLine(pc) if !strings.Contains(file, "gotunnel-ng") { continue } entries = append(entries, fmt.Sprintf("%s %d", file, line)) } if len(entries) > 0 { s := strings.Join(entries, "\n") stats[s]++ } } var traces []string for trace, _ := range stats { traces = append(traces, trace) } sort.Strings(traces) for _, trace := range traces { p("%d\n", stats[trace]) p("%s\n", trace) } }) } else { log.Fatalf("unknown argument %s", arg) } } <-(make(chan bool)) }