command.go 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215
  1. package console
  2. import (
  3. "fmt"
  4. "leafstalk/conf"
  5. "leafstalk/log"
  6. "leafstalk/module/handler"
  7. "os"
  8. "path"
  9. "runtime/pprof"
  10. "time"
  11. )
  12. var commands = []Command{
  13. new(CommandHelp),
  14. new(CommandCPUProf),
  15. new(CommandProf),
  16. }
  17. type Command interface {
  18. // must goroutine safe
  19. name() string
  20. // must goroutine safe
  21. help() string
  22. // must goroutine safe
  23. run(args []string) string
  24. }
  25. type ExternalCommand struct {
  26. _name string
  27. _help string
  28. server *handler.Server
  29. }
  30. func (c *ExternalCommand) name() string {
  31. return c._name
  32. }
  33. func (c *ExternalCommand) help() string {
  34. return c._help
  35. }
  36. func (c *ExternalCommand) run(_args []string) string {
  37. args := make([]interface{}, len(_args))
  38. for i, v := range _args {
  39. args[i] = v
  40. }
  41. ret, err := c.server.Call1(c._name, args...)
  42. if err != nil {
  43. return err.Error()
  44. }
  45. output, ok := ret.(string)
  46. if !ok {
  47. return "invalid output type"
  48. }
  49. return output
  50. }
  51. // you must call the function before calling console.Init
  52. // goroutine not safe
  53. func Register(name string, help string, f interface{}, server *handler.Server) {
  54. for _, c := range commands {
  55. if c.name() == name {
  56. log.Fatalf("command %v is already registered", name)
  57. }
  58. }
  59. server.Register(name, f)
  60. c := new(ExternalCommand)
  61. c._name = name
  62. c._help = help
  63. c.server = server
  64. commands = append(commands, c)
  65. }
  66. // help
  67. type CommandHelp struct{}
  68. func (c *CommandHelp) name() string {
  69. return "help"
  70. }
  71. func (c *CommandHelp) help() string {
  72. return "this help text"
  73. }
  74. func (c *CommandHelp) run([]string) string {
  75. output := "Commands:\r\n"
  76. for _, c := range commands {
  77. output += c.name() + " - " + c.help() + "\r\n"
  78. }
  79. output += "quit - exit console"
  80. return output
  81. }
  82. // cpuprof
  83. type CommandCPUProf struct{}
  84. func (c *CommandCPUProf) name() string {
  85. return "cpuprof"
  86. }
  87. func (c *CommandCPUProf) help() string {
  88. return "CPU profiling for the current process"
  89. }
  90. func (c *CommandCPUProf) usage() string {
  91. return "cpuprof writes runtime profiling data in the format expected by \r\n" +
  92. "the pprof visualization tool\r\n\r\n" +
  93. "Usage: cpuprof start|stop\r\n" +
  94. " start - enables CPU profiling\r\n" +
  95. " stop - stops the current CPU profile"
  96. }
  97. func (c *CommandCPUProf) run(args []string) string {
  98. if len(args) == 0 {
  99. return c.usage()
  100. }
  101. switch args[0] {
  102. case "start":
  103. fn := profileName() + ".cpuprof"
  104. f, err := os.Create(fn)
  105. if err != nil {
  106. return err.Error()
  107. }
  108. err = pprof.StartCPUProfile(f)
  109. if err != nil {
  110. f.Close()
  111. return err.Error()
  112. }
  113. return fn
  114. case "stop":
  115. pprof.StopCPUProfile()
  116. return ""
  117. default:
  118. return c.usage()
  119. }
  120. }
  121. func profileName() string {
  122. now := time.Now()
  123. return path.Join(conf.ProfilePath,
  124. fmt.Sprintf("%d%02d%02d_%02d_%02d_%02d",
  125. now.Year(),
  126. now.Month(),
  127. now.Day(),
  128. now.Hour(),
  129. now.Minute(),
  130. now.Second()))
  131. }
  132. // prof
  133. type CommandProf struct{}
  134. func (c *CommandProf) name() string {
  135. return "prof"
  136. }
  137. func (c *CommandProf) help() string {
  138. return "writes a pprof-formatted snapshot"
  139. }
  140. func (c *CommandProf) usage() string {
  141. return "prof writes runtime profiling data in the format expected by \r\n" +
  142. "the pprof visualization tool\r\n\r\n" +
  143. "Usage: prof goroutine|heap|thread|block\r\n" +
  144. " goroutine - stack traces of all current goroutines\r\n" +
  145. " heap - a sampling of all heap allocations\r\n" +
  146. " thread - stack traces that led to the creation of new OS threads\r\n" +
  147. " block - stack traces that led to blocking on synchronization primitives"
  148. }
  149. func (c *CommandProf) run(args []string) string {
  150. if len(args) == 0 {
  151. return c.usage()
  152. }
  153. var (
  154. p *pprof.Profile
  155. fn string
  156. )
  157. switch args[0] {
  158. case "goroutine":
  159. p = pprof.Lookup("goroutine")
  160. fn = profileName() + ".gprof"
  161. case "heap":
  162. p = pprof.Lookup("heap")
  163. fn = profileName() + ".hprof"
  164. case "thread":
  165. p = pprof.Lookup("threadcreate")
  166. fn = profileName() + ".tprof"
  167. case "block":
  168. p = pprof.Lookup("block")
  169. fn = profileName() + ".bprof"
  170. default:
  171. return c.usage()
  172. }
  173. f, err := os.Create(fn)
  174. if err != nil {
  175. return err.Error()
  176. }
  177. defer f.Close()
  178. err = p.WriteTo(f, 0)
  179. if err != nil {
  180. return err.Error()
  181. }
  182. return fn
  183. }