runtime-info.go 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323
  1. package main
  2. import (
  3. "bufio"
  4. "bytes"
  5. "fmt"
  6. "log"
  7. "runtime"
  8. "strings"
  9. )
  10. // reused by GoRuntinesStatus()
  11. var buffer []byte
  12. var output *bytes.Buffer
  13. // This uses the most memory. There has to be a better way.
  14. const DEBUG_THIS bool = false
  15. /*
  16. var GStatus map[string]string = map[string]string{
  17. "idle": "I",
  18. "runnable": "r",
  19. "running": "R",
  20. "syscall": "s",
  21. "waiting": "W",
  22. "dead": "D",
  23. "copystack": "C",
  24. "preempted": "P",
  25. "sleep": "S",
  26. "select": "s", // missing
  27. "chan receive": "<", // missing
  28. "chan send": ">", // missing
  29. }
  30. */
  31. // Map Status to Symbol.
  32. func GStatusMapper(status []byte) byte {
  33. switch status[0] {
  34. case 'c':
  35. if status[1] == 'o' {
  36. return 'C'
  37. }
  38. switch status[5] {
  39. case 'r':
  40. return '<'
  41. case 's':
  42. return '>'
  43. }
  44. return '?'
  45. case 'd':
  46. return 'D'
  47. case 'i':
  48. return 'I'
  49. case 'p':
  50. return 'P'
  51. case 'r':
  52. switch status[4] {
  53. case 'a':
  54. return 'r'
  55. case 'i':
  56. return 'R'
  57. }
  58. return '?'
  59. case 's':
  60. switch status[1] {
  61. case 'y':
  62. return 's'
  63. case 'l':
  64. return 'S'
  65. case 'e':
  66. return 's'
  67. }
  68. return '?'
  69. case 'w':
  70. return 'W'
  71. default:
  72. return '?'
  73. }
  74. }
  75. // Get Go Routines and map the Status. See GStatus.
  76. func GoRoutinesStatus() []byte {
  77. if output == nil {
  78. log.Println("new bytes.Buffer{}")
  79. output = &bytes.Buffer{}
  80. }
  81. output.Reset()
  82. // Map status to letter.
  83. /*
  84. if GStatus == nil {
  85. GStatus = make(map[string]string)
  86. GStatus = map[string]string{
  87. "idle": "I",
  88. "runnable": "r",
  89. "running": "R",
  90. "syscall": "s",
  91. "waiting": "W",
  92. "dead": "D",
  93. "copystack": "C",
  94. "preempted": "P",
  95. "sleep": "S",
  96. "select": "s", // missing
  97. "chan receive": "<", // missing
  98. "chan send": ">", // missing
  99. }
  100. }
  101. */
  102. // 2K works, we don't have to grow any more.
  103. if buffer == nil {
  104. buffer = make([]byte, 2048)
  105. }
  106. read := runtime.Stack(buffer, true)
  107. if read == cap(buffer) {
  108. log.Printf("Increasing buffer from (%d bytes)\n", cap(buffer))
  109. buffer = make([]byte, cap(buffer)+1024)
  110. return GoRoutinesStatus()
  111. }
  112. var buff []byte = buffer[:read]
  113. // fmt.Println(string(buffer[0:read]))
  114. // This is horribly inefficient (memory allocation wise)
  115. var pos, lineend int
  116. var slice []byte
  117. var space, spaceend int
  118. var status []byte
  119. var count int
  120. for pos != -1 {
  121. if DEBUG_THIS {
  122. log.Printf("POS: %d (%d)\n", pos, len(buff))
  123. }
  124. lineend = bytes.Index(buff[pos:], []byte("\n"))
  125. if DEBUG_THIS {
  126. log.Printf("LineEnd: %d\n", lineend)
  127. }
  128. if lineend != -1 {
  129. lineend += pos
  130. slice = buff[pos:lineend]
  131. pos = lineend + 1
  132. } else {
  133. slice = buffer[pos:]
  134. pos = -1
  135. }
  136. // Process line
  137. if bytes.HasPrefix(slice, []byte("goroutine ")) {
  138. if DEBUG_THIS {
  139. log.Printf("GoRoutine %d [%s]\n", count, slice)
  140. count++
  141. }
  142. // Found a gorutine line
  143. space = bytes.Index(slice, []byte{'['})
  144. spaceend = bytes.Index(slice[space+1:], []byte{']'})
  145. if spaceend == -1 {
  146. status = slice[space+1:]
  147. } else {
  148. spaceend += space + 1
  149. // log.Printf("space %d, spaceend %d\n", space, spaceend)
  150. status = slice[space+1 : spaceend]
  151. }
  152. // Ok, status looks like what we're looking for here!
  153. if DEBUG_THIS {
  154. log.Printf("Status: [%s]\n", status)
  155. }
  156. space = bytes.Index(status, []byte{','})
  157. if space != -1 {
  158. status = status[:space]
  159. // log.Printf("Status now: [%s]\n", status)
  160. }
  161. rstatus := GStatusMapper(status)
  162. //rstatus, ok := GStatus[string(status)]
  163. output.WriteByte(rstatus)
  164. /*
  165. if ok {
  166. output.WriteString(rstatus)
  167. } else {
  168. log.Printf("** Status [%s] not found.\n", status)
  169. }
  170. */
  171. }
  172. }
  173. if false {
  174. var reader = bytes.NewReader(buffer[0:read])
  175. var scanner = bufio.NewScanner(reader)
  176. for scanner.Scan() {
  177. var text = scanner.Text()
  178. if strings.HasPrefix(text, "goroutine ") {
  179. // fmt.Println(text)
  180. // goroutine 20 [runnable]:
  181. // goroutine 1 [select, 1 minutes]:
  182. // goroutine 17 [chan receive]:
  183. // Get ID and get Status.
  184. parts := strings.SplitN(text, " ", 3)
  185. /*
  186. gid, err := strconv.Atoi(parts[1])
  187. if err != nil {
  188. continue
  189. }
  190. */
  191. status := parts[2][1 : len(parts[2])-2]
  192. if strings.Contains(status, ",") {
  193. status = strings.Split(status, ",")[0]
  194. }
  195. /*
  196. rstatus := GStatusMapper(status)
  197. output.WriteByte(rstatus)
  198. rstatus, ok := GStatus[status]
  199. if ok {
  200. output.WriteString(rstatus)
  201. } else {
  202. log.Printf("Status %s not found.\n[%s]\n", rstatus, text)
  203. }
  204. */
  205. }
  206. }
  207. }
  208. return output.Bytes()
  209. }
  210. // Return a nicely formatted string representing memory usage.
  211. func ReprMem(size uint64, output *bytes.Buffer) {
  212. if output == nil {
  213. return
  214. }
  215. output.Reset()
  216. var value float64 = float64(size)
  217. var units byte = ' '
  218. if value > 1024 {
  219. // In KB
  220. units = 'K'
  221. value /= 1024
  222. }
  223. if value > 1024 {
  224. // In MB
  225. units = 'M'
  226. value /= 1024
  227. }
  228. if value > 1024 {
  229. // Im GB
  230. units = 'G'
  231. value /= 1024
  232. }
  233. fmt.Fprintf(output, "%0.2f", value)
  234. if output.Len() > 4 {
  235. output.Reset()
  236. fmt.Fprintf(output, "%0.1f", value)
  237. }
  238. if output.Len() > 4 {
  239. output.Reset()
  240. fmt.Fprintf(output, "%0.0f", value)
  241. }
  242. if units != ' ' {
  243. output.WriteByte(units)
  244. }
  245. }
  246. // Return array of memory usage information
  247. func Memory(result *map[string]*bytes.Buffer) {
  248. var memstats runtime.MemStats
  249. runtime.ReadMemStats(&memstats)
  250. if (*result)["Sys"] == nil {
  251. (*result)["Sys"] = &bytes.Buffer{}
  252. }
  253. ReprMem(memstats.Sys, (*result)["Sys"])
  254. // ReprMem(memstats.HeapAlloc, (*result)["Heap"])
  255. /*
  256. if (*result)["HeapSys"] == nil {
  257. (*result)["HeapSys"] = &bytes.Buffer{}
  258. }
  259. */
  260. if (*result)["Heap"] == nil {
  261. (*result)["Heap"] = &bytes.Buffer{}
  262. }
  263. ReprMem(memstats.HeapAlloc, (*result)["Heap"])
  264. if (*result)["HeapSys"] == nil {
  265. (*result)["HeapSys"] = &bytes.Buffer{}
  266. }
  267. ReprMem(memstats.HeapSys, (*result)["MeapSys"])
  268. if (*result)["StackSys"] == nil {
  269. (*result)["StackSys"] = &bytes.Buffer{}
  270. }
  271. ReprMem(memstats.StackSys, (*result)["StackSys"])
  272. // Don't scale the Number of GCs, it is good as-is.
  273. if (*result)["NumGC"] == nil {
  274. (*result)["NumGC"] = &bytes.Buffer{}
  275. }
  276. (*result)["NumGC"].Reset()
  277. fmt.Fprintf((*result)["NumGC"], "%d", memstats.NumGC)
  278. // ReprMem(uint64(memstats.NumGC), (*result)["NumGC"])
  279. /*
  280. (*result)["HeapInUse"] = ReprMem(memstats.HeapInuse)
  281. (*result)["HeapSys"] = ReprMem(memstats.HeapSys)
  282. (*result)["Sys"] = ReprMem(memstats.Sys)
  283. (*result)["StackInUse"] = ReprMem(memstats.StackInuse)
  284. (*result)["StackSys"] = ReprMem(memstats.StackSys)
  285. (*result)["GCSys"] = ReprMem(memstats.GCSys)
  286. */
  287. }