概要
以下は自分用のメモです。よく忘れるので、ここにメモメモ。。
常に必要じゃないんですが、たまにシグナルをちゃんとトラップしていろいろ動いてくれるアプリを作るときがあります。
そんなときに、JavaVMみたいにSIGQUIT受けたらスレッドダンプ(Goの場合はゴルーチンダンプ)を出力できたら便利なときがあります。
サンプル
// OVERVIEW // // JAVA VM は SIGQUIT を受け取るとスレッドダンプを出力します。 // Goで同じような動きをするサンプルです。 // // REFERENCE // // - https://stackoverflow.com/a/27398062 package main import ( "context" "log" "os" "os/signal" "runtime" "syscall" "time" ) const ( Interval = 1 * time.Second TimeLimit = 10 * time.Second ) var ( appLog = log.New(os.Stdout, "", 0) tickLog = log.New(os.Stdout, "", log.Ltime) dumpLog = log.New(os.Stderr, "", 0) ) func main() { // ------------------------------------------------ // Overview // ------------------------------------------------ // [x] 10秒たったらプログラム終了 // [x] SIGINTを受けたらプログラム終了 // [x] SIGQUITを受けたらスレッドダンプを出力 // [x] 1秒毎の経過ログを出力 // ------------------------------------------------ var ( rootCtx = context.Background() mainCtx, mainCxl = context.WithTimeout(rootCtx, TimeLimit) ) defer mainCxl() var ( sigIntCh = make(chan os.Signal, 1) sigQuitCh = make(chan os.Signal, 1) ) defer close(sigIntCh) defer close(sigQuitCh) signal.Notify(sigIntCh, syscall.SIGINT) signal.Notify(sigQuitCh, syscall.SIGQUIT) var ( ticker = time.NewTicker(Interval) count = 0 ) defer ticker.Stop() LOOP: for { select { case <-mainCtx.Done(): appLog.Println("Timed out") break LOOP case <-sigIntCh: appLog.Println(" >>> Recv SIGINT") break LOOP case <-sigQuitCh: buf := make([]byte, 1<<25) dumpLog.Printf(" >>> *** DUMP ***\n\n%s\n", buf[:runtime.Stack(buf, true)]) case <-ticker.C: count++ tickLog.Println(count) } } appLog.Println("DONE") }
実行すると以下のようになります。
適当なタイミングでSIGQUIT送ってみてみると、ちゃんとダンプ出てますね。
Linuxでは、SIGQUITは Ctrl+\
で送れます。
gitpod /workspace/try-golang (master) $ go run examples/singleapp/sigquit_like_javavm/main.go 04:34:50 1 04:34:51 2 04:34:52 3 ^\ >>> *** DUMP *** goroutine 1 [running]: main.main() /workspace/try-golang/examples/singleapp/sigquit_like_javavm/main.go:76 +0x2b5 goroutine 17 [syscall]: os/signal.signal_recv() /home/gitpod/go/src/runtime/sigqueue.go:169 +0x98 os/signal.loop() /home/gitpod/go/src/os/signal/signal_unix.go:24 +0x19 created by os/signal.Notify.func1.1 /home/gitpod/go/src/os/signal/signal.go:151 +0x2c 04:34:53 4 04:34:54 5 04:34:55 6 ^\ >>> *** DUMP *** goroutine 1 [running]: main.main() /workspace/try-golang/examples/singleapp/sigquit_like_javavm/main.go:76 +0x2b5 goroutine 17 [syscall]: os/signal.signal_recv() /home/gitpod/go/src/runtime/sigqueue.go:169 +0x98 os/signal.loop() /home/gitpod/go/src/os/signal/signal_unix.go:24 +0x19 created by os/signal.Notify.func1.1 /home/gitpod/go/src/os/signal/signal.go:151 +0x2c 04:34:56 7 04:34:57 8 ^C >>> Recv SIGINT DONE
参考情報
過去の記事については、以下のページからご参照下さい。
サンプルコードは、以下の場所で公開しています。