123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323 |
- package main
- import (
- "bufio"
- "bytes"
- "fmt"
- "log"
- "runtime"
- "strings"
- )
- // reused by GoRuntinesStatus()
- var buffer []byte
- var output *bytes.Buffer
- // This uses the most memory. There has to be a better way.
- const DEBUG_THIS bool = false
- /*
- var GStatus map[string]string = map[string]string{
- "idle": "I",
- "runnable": "r",
- "running": "R",
- "syscall": "s",
- "waiting": "W",
- "dead": "D",
- "copystack": "C",
- "preempted": "P",
- "sleep": "S",
- "select": "s", // missing
- "chan receive": "<", // missing
- "chan send": ">", // missing
- }
- */
- // Map Status to Symbol.
- func GStatusMapper(status []byte) byte {
- switch status[0] {
- case 'c':
- if status[1] == 'o' {
- return 'C'
- }
- switch status[5] {
- case 'r':
- return '<'
- case 's':
- return '>'
- }
- return '?'
- case 'd':
- return 'D'
- case 'i':
- return 'I'
- case 'p':
- return 'P'
- case 'r':
- switch status[4] {
- case 'a':
- return 'r'
- case 'i':
- return 'R'
- }
- return '?'
- case 's':
- switch status[1] {
- case 'y':
- return 's'
- case 'l':
- return 'S'
- case 'e':
- return 's'
- }
- return '?'
- case 'w':
- return 'W'
- default:
- return '?'
- }
- }
- // Get Go Routines and map the Status. See GStatus.
- func GoRoutinesStatus() []byte {
- if output == nil {
- log.Println("new bytes.Buffer{}")
- output = &bytes.Buffer{}
- }
- output.Reset()
- // Map status to letter.
- /*
- if GStatus == nil {
- GStatus = make(map[string]string)
- GStatus = map[string]string{
- "idle": "I",
- "runnable": "r",
- "running": "R",
- "syscall": "s",
- "waiting": "W",
- "dead": "D",
- "copystack": "C",
- "preempted": "P",
- "sleep": "S",
- "select": "s", // missing
- "chan receive": "<", // missing
- "chan send": ">", // missing
- }
- }
- */
- // 2K works, we don't have to grow any more.
- if buffer == nil {
- buffer = make([]byte, 2048)
- }
- read := runtime.Stack(buffer, true)
- if read == cap(buffer) {
- log.Printf("Increasing buffer from (%d bytes)\n", cap(buffer))
- buffer = make([]byte, cap(buffer)+1024)
- return GoRoutinesStatus()
- }
- var buff []byte = buffer[:read]
- // fmt.Println(string(buffer[0:read]))
- // This is horribly inefficient (memory allocation wise)
- var pos, lineend int
- var slice []byte
- var space, spaceend int
- var status []byte
- var count int
- for pos != -1 {
- if DEBUG_THIS {
- log.Printf("POS: %d (%d)\n", pos, len(buff))
- }
- lineend = bytes.Index(buff[pos:], []byte("\n"))
- if DEBUG_THIS {
- log.Printf("LineEnd: %d\n", lineend)
- }
- if lineend != -1 {
- lineend += pos
- slice = buff[pos:lineend]
- pos = lineend + 1
- } else {
- slice = buffer[pos:]
- pos = -1
- }
- // Process line
- if bytes.HasPrefix(slice, []byte("goroutine ")) {
- if DEBUG_THIS {
- log.Printf("GoRoutine %d [%s]\n", count, slice)
- count++
- }
- // Found a gorutine line
- space = bytes.Index(slice, []byte{'['})
- spaceend = bytes.Index(slice[space+1:], []byte{']'})
- if spaceend == -1 {
- status = slice[space+1:]
- } else {
- spaceend += space + 1
- // log.Printf("space %d, spaceend %d\n", space, spaceend)
- status = slice[space+1 : spaceend]
- }
- // Ok, status looks like what we're looking for here!
- if DEBUG_THIS {
- log.Printf("Status: [%s]\n", status)
- }
- space = bytes.Index(status, []byte{','})
- if space != -1 {
- status = status[:space]
- // log.Printf("Status now: [%s]\n", status)
- }
- rstatus := GStatusMapper(status)
- //rstatus, ok := GStatus[string(status)]
- output.WriteByte(rstatus)
- /*
- if ok {
- output.WriteString(rstatus)
- } else {
- log.Printf("** Status [%s] not found.\n", status)
- }
- */
- }
- }
- if false {
- var reader = bytes.NewReader(buffer[0:read])
- var scanner = bufio.NewScanner(reader)
- for scanner.Scan() {
- var text = scanner.Text()
- if strings.HasPrefix(text, "goroutine ") {
- // fmt.Println(text)
- // goroutine 20 [runnable]:
- // goroutine 1 [select, 1 minutes]:
- // goroutine 17 [chan receive]:
- // Get ID and get Status.
- parts := strings.SplitN(text, " ", 3)
- /*
- gid, err := strconv.Atoi(parts[1])
- if err != nil {
- continue
- }
- */
- status := parts[2][1 : len(parts[2])-2]
- if strings.Contains(status, ",") {
- status = strings.Split(status, ",")[0]
- }
- /*
- rstatus := GStatusMapper(status)
- output.WriteByte(rstatus)
- rstatus, ok := GStatus[status]
- if ok {
- output.WriteString(rstatus)
- } else {
- log.Printf("Status %s not found.\n[%s]\n", rstatus, text)
- }
- */
- }
- }
- }
- return output.Bytes()
- }
- // Return a nicely formatted string representing memory usage.
- func ReprMem(size uint64, output *bytes.Buffer) {
- if output == nil {
- return
- }
- output.Reset()
- var value float64 = float64(size)
- var units byte = ' '
- if value > 1024 {
- // In KB
- units = 'K'
- value /= 1024
- }
- if value > 1024 {
- // In MB
- units = 'M'
- value /= 1024
- }
- if value > 1024 {
- // Im GB
- units = 'G'
- value /= 1024
- }
- fmt.Fprintf(output, "%0.2f", value)
- if output.Len() > 4 {
- output.Reset()
- fmt.Fprintf(output, "%0.1f", value)
- }
- if output.Len() > 4 {
- output.Reset()
- fmt.Fprintf(output, "%0.0f", value)
- }
- if units != ' ' {
- output.WriteByte(units)
- }
- }
- // Return array of memory usage information
- func Memory(result *map[string]*bytes.Buffer) {
- var memstats runtime.MemStats
- runtime.ReadMemStats(&memstats)
- if (*result)["Sys"] == nil {
- (*result)["Sys"] = &bytes.Buffer{}
- }
- ReprMem(memstats.Sys, (*result)["Sys"])
- // ReprMem(memstats.HeapAlloc, (*result)["Heap"])
- /*
- if (*result)["HeapSys"] == nil {
- (*result)["HeapSys"] = &bytes.Buffer{}
- }
- */
- if (*result)["Heap"] == nil {
- (*result)["Heap"] = &bytes.Buffer{}
- }
- ReprMem(memstats.HeapAlloc, (*result)["Heap"])
- if (*result)["HeapSys"] == nil {
- (*result)["HeapSys"] = &bytes.Buffer{}
- }
- ReprMem(memstats.HeapSys, (*result)["MeapSys"])
- if (*result)["StackSys"] == nil {
- (*result)["StackSys"] = &bytes.Buffer{}
- }
- ReprMem(memstats.StackSys, (*result)["StackSys"])
- // Don't scale the Number of GCs, it is good as-is.
- if (*result)["NumGC"] == nil {
- (*result)["NumGC"] = &bytes.Buffer{}
- }
- (*result)["NumGC"].Reset()
- fmt.Fprintf((*result)["NumGC"], "%d", memstats.NumGC)
- // ReprMem(uint64(memstats.NumGC), (*result)["NumGC"])
- /*
- (*result)["HeapInUse"] = ReprMem(memstats.HeapInuse)
- (*result)["HeapSys"] = ReprMem(memstats.HeapSys)
- (*result)["Sys"] = ReprMem(memstats.Sys)
- (*result)["StackInUse"] = ReprMem(memstats.StackInuse)
- (*result)["StackSys"] = ReprMem(memstats.StackSys)
- (*result)["GCSys"] = ReprMem(memstats.GCSys)
- */
- }
|