├── Makefile ├── RequestDispatcher.go ├── main.go ├── README.md ├── .gitignore ├── Requests.go ├── .gitattributes ├── ServiceStateDispatcher.go ├── BatchJob.go ├── RequestSelector.go ├── TopDispather.go ├── RequestFabric.go ├── SystemMonitorDispatcher.go ├── Top_test.go ├── SystemStat.go ├── Top.go ├── SystemStatLinux.go ├── index.html └── WebService.go /Makefile: -------------------------------------------------------------------------------- 1 | GO=go 2 | PROGRAMM=wtop 3 | PREFIX=/usr/ 4 | 5 | $(PROGRAMM): 6 | $(GO) build -o $(PROGRAMM) 7 | install: $(PROGRAMM) 8 | cp -rfv ./wtop $(PREFIX)/bin 9 | clean: 10 | rm -fv ./wtop 11 | -------------------------------------------------------------------------------- /RequestDispatcher.go: -------------------------------------------------------------------------------- 1 | // RequestDispatcher 2 | package main 3 | 4 | import "net/http" 5 | 6 | type RequestDispatcher interface { 7 | Dispatch(request Request, responseWriter http.ResponseWriter, httpRequest *http.Request) error 8 | } 9 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | // WebTop project main.go 2 | package main 3 | 4 | import "log" 5 | 6 | func main() { 7 | topJson := new(TopJsonService) 8 | listenPort := 9977 9 | if err := topJson.Start(listenPort); err != nil { 10 | log.Println(err) 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | WebTop 2 | ====== 3 | 4 | This is simple task manger for Linux system with Web interface. No dependencies, only WebTop binary and index.html in same directory 5 | 6 | ######3 rules. 7 | 8 | 1. Access to /proc/ directory 9 | 2. index.html and WebTop binary in same directory 10 | 3. Go to http://localhost:9977/index.html in normal browser 11 | 12 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files, Static and Dynamic libs (Shared Objects) 2 | *.o 3 | *.a 4 | *.so 5 | 6 | # Folders 7 | _obj 8 | _test 9 | 10 | # Architecture specific extensions/prefixes 11 | *.[568vq] 12 | [568vq].out 13 | 14 | *.cgo1.go 15 | *.cgo2.c 16 | _cgo_defun.c 17 | _cgo_gotypes.go 18 | _cgo_export.* 19 | 20 | _testmain.go 21 | 22 | *.exe 23 | *.test 24 | *.prof 25 | 26 | WebTop 27 | wtop 28 | -------------------------------------------------------------------------------- /Requests.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | const ( 4 | ServiceStatus = iota 5 | SystemMonitor 6 | TopProcess 7 | KillProcess 8 | ) 9 | 10 | //basic http request with writer 11 | type Request interface { 12 | RequestType() int 13 | } 14 | 15 | type BasicRequest struct { 16 | Type int `json:"Type,string,omitempty"` 17 | } 18 | 19 | func (basicRequest BasicRequest) RequestType() int { 20 | return basicRequest.Type 21 | } 22 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Custom for Visual Studio 5 | *.cs diff=csharp 6 | *.sln merge=union 7 | *.csproj merge=union 8 | *.vbproj merge=union 9 | *.fsproj merge=union 10 | *.dbproj merge=union 11 | 12 | # Standard to msysgit 13 | *.doc diff=astextplain 14 | *.DOC diff=astextplain 15 | *.docx diff=astextplain 16 | *.DOCX diff=astextplain 17 | *.dot diff=astextplain 18 | *.DOT diff=astextplain 19 | *.pdf diff=astextplain 20 | *.PDF diff=astextplain 21 | *.rtf diff=astextplain 22 | *.RTF diff=astextplain 23 | -------------------------------------------------------------------------------- /ServiceStateDispatcher.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "net/http" 4 | import "encoding/json" 5 | import "errors" 6 | import "log" 7 | 8 | type ServiceStateRequest struct { 9 | BasicRequest 10 | } 11 | 12 | type ServiceStateResponse struct { 13 | UpTime bool 14 | WorkFromRoot bool 15 | ProcStat bool 16 | MemInfo bool 17 | } 18 | 19 | type ServiceStateDispatcher struct { 20 | } 21 | 22 | func (serviceStateDispatcher *ServiceStateDispatcher) Dispatch(request Request, responseWriter http.ResponseWriter, httpRequest *http.Request) error { 23 | //this is service is not need lock 24 | serviceState := ServiceStateResponse{true, true, true, true} 25 | js, err := json.Marshal(serviceState) 26 | if err != nil { 27 | http.Error(responseWriter, err.Error(), http.StatusInternalServerError) 28 | stErr := "error: Can't service state response" 29 | log.Println(stErr) 30 | responseWriter.Write(js) 31 | return errors.New(stErr) 32 | } 33 | responseWriter.Header().Set("Content-Type", "application/json") 34 | responseWriter.Write(js) 35 | return nil 36 | } 37 | -------------------------------------------------------------------------------- /BatchJob.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "errors" 4 | 5 | type BatchJob struct { 6 | Job func() 7 | runJob bool 8 | done chan bool 9 | } 10 | 11 | func (batchJob *BatchJob) Start() error { 12 | if batchJob.runJob { 13 | 14 | return errors.New("error: can't stop not stopped job") 15 | } 16 | if batchJob.Job == nil { 17 | return errors.New("error: empty job function") 18 | } 19 | 20 | if !batchJob.runJob { 21 | batchJob.done = make(chan bool, 1) 22 | } 23 | 24 | go batchJob.execution(batchJob.done) 25 | batchJob.runJob = true 26 | return nil 27 | } 28 | func (batchJob *BatchJob) IsRunning() bool { 29 | return batchJob.runJob 30 | 31 | } 32 | 33 | func (batchJob *BatchJob) Stop() error { 34 | if !batchJob.runJob { 35 | return errors.New("error: can't stop not stopted job") 36 | } 37 | batchJob.runJob = false 38 | isDone := <-batchJob.done 39 | if isDone { 40 | close(batchJob.done) 41 | return nil 42 | } 43 | return errors.New("error: failed stop job") 44 | } 45 | 46 | func (batchJob *BatchJob) execution(done chan bool) { 47 | for { 48 | if batchJob.runJob { 49 | batchJob.Job() 50 | } else { 51 | done <- true 52 | return 53 | } 54 | 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /RequestSelector.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "errors" 4 | import "net/http" 5 | import "log" 6 | 7 | type RequestSelector struct { 8 | selectorRequestMap map[int]RequestDispatcher 9 | } 10 | 11 | func (requestSelector *RequestSelector) Dispatch(request Request, responseWriter http.ResponseWriter, httpRequest *http.Request) error { 12 | //I don't protect multiple read in different thread 13 | if selector, contains := requestSelector.selectorRequestMap[request.RequestType()]; !contains { 14 | stErr := "error: Usupported message type" 15 | log.Println(stErr) 16 | return errors.New(stErr) 17 | } else { 18 | return selector.Dispatch(request, responseWriter, httpRequest) 19 | } 20 | } 21 | 22 | func (requestSelector *RequestSelector) Init() error { 23 | //create map 24 | requestSelector.selectorRequestMap = make(map[int]RequestDispatcher) 25 | if requestSelector.selectorRequestMap == nil { 26 | stErr := "error: Can't create map" 27 | log.Println(stErr) 28 | return errors.New(stErr) 29 | } 30 | requestSelector.selectorRequestMap[ServiceStatus] = new(ServiceStateDispatcher) 31 | systemMonitorDispatcher := new(SystemMonitorDispatcher) 32 | systemMonitorDispatcher.StartMeasure() 33 | requestSelector.selectorRequestMap[SystemMonitor] = systemMonitorDispatcher 34 | //same dispatcher for two message 35 | topProcessDispatcher := new(TopDispatcher) 36 | topProcessDispatcher.StartMeasure() 37 | requestSelector.selectorRequestMap[TopProcess] = topProcessDispatcher 38 | requestSelector.selectorRequestMap[KillProcess] = topProcessDispatcher 39 | return nil 40 | } 41 | -------------------------------------------------------------------------------- /TopDispather.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "net/http" 4 | import "errors" 5 | import "encoding/json" 6 | import "log" 7 | 8 | type TopResponse struct { 9 | ProcessItems []ProcessItem 10 | } 11 | 12 | type KillResponse struct { 13 | KilResult string 14 | } 15 | 16 | type KillRequest struct { 17 | BasicRequest 18 | Pid int `Pid:"Type,string,omitempty"` 19 | } 20 | type TopRequest struct { 21 | BasicRequest 22 | } 23 | 24 | type TopDispatcher struct { 25 | top Top 26 | } 27 | 28 | func (serviceStateDispatcher *TopDispatcher) Dispatch(request Request, responseWriter http.ResponseWriter, httpRequest *http.Request) error { 29 | switch request.RequestType() { 30 | case TopProcess: 31 | topProcessList, err := serviceStateDispatcher.top.GetProcessList() 32 | if err != nil { 33 | stErr := "error get process list " 34 | log.Println(stErr) 35 | return errors.New("error get process list ") 36 | } 37 | topResponse := TopResponse{ProcessItems: topProcessList} 38 | tr, err := json.Marshal(topResponse) 39 | if err != nil { 40 | http.Error(responseWriter, err.Error(), http.StatusInternalServerError) 41 | return err 42 | } 43 | responseWriter.Header().Set("Content-Type", "application/json") 44 | responseWriter.Write(tr) 45 | case KillProcess: 46 | killRequest := request.(KillRequest) 47 | err := serviceStateDispatcher.top.KillProcess(killRequest.Pid) 48 | 49 | var killResultMsg string 50 | if err != nil { 51 | killResultMsg = err.Error() 52 | } else { 53 | killResultMsg = "Send kill top process" + string(killRequest.Pid) 54 | } 55 | killResponseResponse := KillResponse{KilResult: killResultMsg} 56 | tr, err := json.Marshal(killResponseResponse) 57 | if err != nil { 58 | http.Error(responseWriter, err.Error(), http.StatusInternalServerError) 59 | return err 60 | } 61 | responseWriter.Header().Set("Content-Type", "application/json") 62 | responseWriter.Write(tr) 63 | } 64 | return nil 65 | } 66 | func (serviceStateDispatcher *TopDispatcher) StartMeasure() { 67 | serviceStateDispatcher.top.StartCollectInfo() 68 | } 69 | -------------------------------------------------------------------------------- /RequestFabric.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "errors" 4 | import "io/ioutil" 5 | import "net/http" 6 | import "encoding/json" 7 | import "log" 8 | 9 | type JsonFabric struct { 10 | } 11 | 12 | //parse json message 13 | func (fabric *JsonFabric) ProduceJsonRequest(request *http.Request) (Request, error) { 14 | bodyData, err := ioutil.ReadAll(request.Body) 15 | if err != nil { 16 | stErr := "error: Can't read request body" 17 | log.Println(stErr) 18 | return nil, errors.New(stErr) 19 | } 20 | defer request.Body.Close() 21 | 22 | var basicRequest BasicRequest 23 | err = json.Unmarshal(bodyData, &basicRequest) 24 | if err != nil { 25 | stErr := "error: Can't parse basic data" 26 | log.Println(stErr) 27 | return nil, errors.New(stErr) 28 | } 29 | switch basicRequest.Type { 30 | case ServiceStatus: 31 | var serviceStateRequest ServiceStateRequest 32 | err := json.Unmarshal(bodyData, &serviceStateRequest) 33 | if err != nil { 34 | stErr := "error: Can't parse service state request" 35 | log.Println(stErr) 36 | return nil, errors.New("error: Can't parse service state request") 37 | } 38 | return serviceStateRequest, nil 39 | case SystemMonitor: 40 | var systemStateRequest SystemStateRequest 41 | err := json.Unmarshal(bodyData, &systemStateRequest) 42 | if err != nil { 43 | stErr := "error: Can't parse system state request" 44 | log.Println(stErr) 45 | return nil, errors.New(stErr) 46 | } 47 | return systemStateRequest, nil 48 | case TopProcess: 49 | var topRequest TopRequest 50 | err := json.Unmarshal(bodyData, &topRequest) 51 | if err != nil { 52 | stErr := "error: Can't parse top request" 53 | log.Println(stErr) 54 | return nil, errors.New(stErr) 55 | 56 | } 57 | return topRequest, nil 58 | case KillProcess: 59 | var killRequest KillRequest 60 | err := json.Unmarshal(bodyData, &killRequest) 61 | if err != nil { 62 | stErr := "error: Can't parse top request" 63 | log.Println(stErr) 64 | return nil, errors.New(stErr) 65 | } 66 | return killRequest, nil 67 | 68 | } 69 | 70 | return nil, errors.New("error: Unknown request type") 71 | } 72 | -------------------------------------------------------------------------------- /SystemMonitorDispatcher.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "net/http" 4 | import "encoding/json" 5 | 6 | import "time" 7 | import "errors" 8 | import "log" 9 | 10 | type SystemStateRequest struct { 11 | BasicRequest 12 | } 13 | 14 | type SystemMonitorResponse struct { 15 | CPUUsage CPUAverage 16 | MemSample MemSample 17 | } 18 | 19 | type SystemMonitorDispatcher struct { 20 | lastCPUSample CPUSample 21 | lastCPUAverage CPUAverage 22 | measureJob BatchJob 23 | lastRequestTime time.Time 24 | } 25 | 26 | func (serviceStateDispatcher *SystemMonitorDispatcher) getCPUUsage() CPUAverage { 27 | lastCPUAverage := serviceStateDispatcher.lastCPUAverage 28 | return lastCPUAverage 29 | 30 | } 31 | 32 | func (serviceStateDispatcher *SystemMonitorDispatcher) measureCPU() { 33 | for { 34 | serviceStateDispatcher.lastCPUAverage = GetCPUAverage(serviceStateDispatcher.lastCPUSample, GetCPUSample()) 35 | serviceStateDispatcher.lastCPUSample = GetCPUSample() 36 | time.Sleep(500 * time.Millisecond) 37 | secondLastRequest := time.Now().Sub(serviceStateDispatcher.lastRequestTime) 38 | if secondLastRequest.Seconds() > 5 { 39 | 40 | log.Println("info: sleep measure cpu job") 41 | serviceStateDispatcher.measureJob.Stop() 42 | 43 | } 44 | } 45 | } 46 | 47 | func (serviceStateDispatcher *SystemMonitorDispatcher) StartMeasure() error { 48 | serviceStateDispatcher.measureJob.Job = serviceStateDispatcher.measureCPU 49 | serviceStateDispatcher.lastRequestTime = time.Now() 50 | err := serviceStateDispatcher.measureJob.Start() 51 | if err != nil { 52 | stErr := "error: Can't start measure job" 53 | log.Println(stErr) 54 | return errors.New(stErr) 55 | } 56 | return nil 57 | } 58 | func (serviceStateDispatcher *SystemMonitorDispatcher) Stopmeasure() error { 59 | err := serviceStateDispatcher.measureJob.Stop() 60 | if err != nil { 61 | stErr := "error: Can't start measure job" 62 | log.Println(stErr) 63 | return errors.New("error: Can't stop measure job\n") 64 | } 65 | return nil 66 | } 67 | 68 | func (serviceStateDispatcher *SystemMonitorDispatcher) Dispatch(request Request, responseWriter http.ResponseWriter, httpRequest *http.Request) error { 69 | if !serviceStateDispatcher.measureJob.runJob { 70 | log.Println("info: start measure cpu job") 71 | serviceStateDispatcher.measureJob.Start() 72 | } 73 | 74 | systemInfo := SystemMonitorResponse{CPUUsage: serviceStateDispatcher.getCPUUsage(), MemSample: GetMemSample()} 75 | js, err := json.Marshal(systemInfo) 76 | if err != nil { 77 | http.Error(responseWriter, err.Error(), http.StatusInternalServerError) 78 | stErr := "error: Can't create system state response" 79 | log.Println(stErr) 80 | responseWriter.Write(js) 81 | return errors.New(stErr) 82 | } 83 | responseWriter.Header().Set("Content-Type", "application/json") 84 | responseWriter.Write(js) 85 | serviceStateDispatcher.lastRequestTime = time.Now() 86 | return nil 87 | } 88 | -------------------------------------------------------------------------------- /Top_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "testing" 4 | import "time" 5 | import "fmt" 6 | 7 | /*func TestTopList(t *testing.T) { 8 | top := new(Top) 9 | top.StartCollectInfo() 10 | for i := 0; i < 10; i++ { 11 | processList, err := top.GetProcessList() 12 | time.Sleep(500 * time.Millisecond) 13 | if processList == nil { 14 | t.Errorf("GetProcessList return null ") 15 | return 16 | } else if err != nil { 17 | t.Errorf("") 18 | return 19 | } 20 | } 21 | 22 | } 23 | 24 | func TestTopCpu(t *testing.T) { 25 | top := new(Top) 26 | top.StartCollectInfo() 27 | for i := 0; i < 5; i++ { 28 | processList, err := top.GetProcessList() 29 | time.Sleep(500 * time.Millisecond) 30 | if processList == nil { 31 | t.Errorf("GetProcessList return null ") 32 | return 33 | } else if err != nil { 34 | t.Errorf("") 35 | return 36 | } else { 37 | for _, element := range processList { 38 | if element.Cpu != 0 { 39 | fmt.Printf("Name: %v Usage: %v \n", element.Name, int32(element.Cpu*100)) 40 | } 41 | } 42 | fmt.Println("_______________________________________________________________________________________________________________________________________________________________________") 43 | } 44 | } 45 | 46 | } 47 | func TestTopTicks(t *testing.T) { 48 | top := new(Top) 49 | top.StartCollectInfo() 50 | for i := 0; i < 5; i++ { 51 | Ticks, err := top.getTicksProcessor() 52 | time.Sleep(500 * time.Millisecond) 53 | if err != nil { 54 | t.Errorf("") 55 | return 56 | } else { 57 | fmt.Printf("Ticks: %v \n", Ticks) 58 | fmt.Println("_______________________________________________________________________________________________________________________________________________________________________") 59 | } 60 | } 61 | 62 | }*/ 63 | 64 | func ThreadTest() { 65 | fmt.Println("Do some job") 66 | time.Sleep(50 * time.Millisecond) 67 | } 68 | 69 | func StopThreadTest(batchJob *BatchJob) { 70 | for i := 0; i < 50; i++ { 71 | //fmt.Println("Try stop") 72 | batchJob.Stop() 73 | time.Sleep(120 * time.Millisecond) 74 | } 75 | } 76 | 77 | func StartThreadTest(batchJob *BatchJob) { 78 | for i := 0; i < 50; i++ { 79 | //fmt.Println("Try start") 80 | batchJob.Start() 81 | time.Sleep(90 * time.Millisecond) 82 | } 83 | } 84 | 85 | func TestBatchJob(t *testing.T) { 86 | batchJob := BatchJob{} 87 | err := batchJob.Start() 88 | if err == nil { 89 | t.Errorf("error: Test start not inited job failed ") 90 | } 91 | 92 | err = batchJob.Stop() 93 | if err == nil { 94 | t.Errorf("error: Test stop not inited job failed ") 95 | } 96 | batchJob.Job = ThreadTest 97 | batchJob.Start() 98 | time.Sleep(500 * time.Millisecond) 99 | err = batchJob.Stop() 100 | if err != nil { 101 | t.Errorf("error: Test stop failed ") 102 | } 103 | go StartThreadTest(&batchJob) 104 | go StopThreadTest(&batchJob) 105 | time.Sleep(500 * time.Millisecond) 106 | for i := 0; i < 100; i++ { 107 | time.Sleep(100 * time.Millisecond) 108 | fmt.Println(batchJob.IsRunning()) 109 | } 110 | time.Sleep(10000 * time.Millisecond) 111 | } 112 | -------------------------------------------------------------------------------- /SystemStat.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013 Phillip Bond 2 | // Licensed under the MIT License 3 | // see file LICENSE 4 | 5 | package main 6 | 7 | import "time" 8 | 9 | // CPUSample is an object that represents the breakdown of time spent by the 10 | // CPU in various types of tasks. Two CPUSamples are required to find the 11 | // average usage over time, represented by the CPUAverage object. The CPUSample 12 | // is taken from the line "cpu" from /proc/stat in the Linux kernel. 13 | // 14 | // 15 | // Summarized from the proc(5) man page: 16 | // /proc/stat : 17 | // kernel/system statistics. Varies with architecture. 18 | type CPUSample struct { 19 | User uint64 // time spent in user mode 20 | Nice uint64 // time spent in user mode with low priority (nice) 21 | System uint64 // time spent in system mode 22 | Idle uint64 // time spent in the idle task 23 | Iowait uint64 // time spent waiting for I/O to complete (since Linux 2.5.41) 24 | Irq uint64 // time spent servicing interrupts (since 2.6.0-test4) 25 | SoftIrq uint64 // time spent servicing softirqs (since 2.6.0-test4) 26 | Steal uint64 // time spent in other OSes when running in a virtualized environment 27 | Guest uint64 // time spent running a virtual CPU for guest operating systems under the control of the Linux kernel. 28 | Name string // name of the line in /proc/stat; cpu, cpu1, etc 29 | Time time.Time // when the sample was taken 30 | Total uint64 // total of all time fields 31 | } 32 | 33 | type ProcCPUSample struct { 34 | User float64 // time spent in user mode 35 | System float64 // time spent in system mode 36 | Time time.Time // when the sample was taken 37 | Total float64 // total of all time fields 38 | ProcMemUsedK int64 39 | } 40 | 41 | type ProcCPUAverage struct { 42 | UserPct float64 // time spent in user mode 43 | SystemPct float64 // time spent in system mode 44 | TotalPct float64 // total of all time fields 45 | PossiblePct float64 // total of all time fields 46 | CumulativeTotalPct float64 // total of all time throughout process life 47 | Time time.Time // when the sample was taken 48 | Seconds float64 // how many seconds between the two samples 49 | } 50 | 51 | // SimpleCPUAverage is an object that represents the average cpu usage over a 52 | // time period. It is calculated by taking the difference between two 53 | // CPUSamples (whose units are clock ticks), dividing by the number of elapsed 54 | // ticks between the samples, and converting to a percent. It is a simplified version of the CPUAverage in that it only accounts for time in the Idle task and all other time (Busy). 55 | type SimpleCPUAverage struct { 56 | BusyPct float64 // percent of time spent by CPU performing all non-idle tasks 57 | IdlePct float64 // percent of time spent by CPU in the idle task 58 | } 59 | 60 | // CPUAverage is an object that represents the average cpu usage over a 61 | // time period. It is calculated by taking the difference between two 62 | // CPUSamples (whose units are clock ticks), dividing by the number of elapsed 63 | // ticks between the samples, and converting to a percent. 64 | type CPUAverage struct { 65 | UserPct float64 66 | NicePct float64 67 | SystemPct float64 68 | IdlePct float64 69 | IowaitPct float64 70 | IrqPct float64 71 | SoftIrqPct float64 72 | StealPct float64 73 | GuestPct float64 74 | Time time.Time 75 | Seconds float64 // how many seconds between the two samples 76 | } 77 | 78 | type MemSample struct { 79 | Buffers uint64 80 | Cached uint64 81 | MemTotal uint64 82 | MemUsed uint64 83 | MemFree uint64 84 | SwapTotal uint64 85 | SwapUsed uint64 86 | SwapFree uint64 87 | Time time.Time 88 | } 89 | 90 | type LoadAvgSample struct { 91 | One float64 92 | Five float64 93 | Fifteen float64 94 | Time time.Time 95 | } 96 | 97 | type UptimeSample struct { 98 | Uptime float64 99 | Time time.Time 100 | } 101 | 102 | // GetCPUAverage returns the average cpu usage between two CPUSamples. 103 | func GetCPUAverage(first CPUSample, second CPUSample) CPUAverage { 104 | return getCPUAverage(first, second) 105 | } 106 | 107 | // GetSimpleCPUAverage returns an aggregated average cpu usage between two CPUSamples. 108 | func GetSimpleCPUAverage(first CPUSample, second CPUSample) SimpleCPUAverage { 109 | return getSimpleCPUAverage(first, second) 110 | } 111 | 112 | // GetProcCPUAverage returns the average cpu usage of this running process 113 | func GetProcCPUAverage(first ProcCPUSample, second ProcCPUSample, procUptime float64) (avg ProcCPUAverage) { 114 | return getProcCPUAverage(first, second, procUptime) 115 | } 116 | 117 | // GetCPUSample takes a snapshot of kernel statistics from the /proc/stat file. 118 | func GetCPUSample() (samp CPUSample) { 119 | return getCPUSample("/proc/stat") 120 | } 121 | 122 | // GetProcCPUSample takes a snapshot of kernel statistics from the /proc/stat file. 123 | func GetProcCPUSample() (samp ProcCPUSample) { 124 | return getProcCPUSample() 125 | } 126 | 127 | // GetUptime takes a snapshot of load info from the /proc/loadavg file. 128 | func GetUptime() (samp UptimeSample) { 129 | return getUptime("/proc/uptime") 130 | } 131 | 132 | // GetLoadAvgSample takes a snapshot of load info from the /proc/loadavg file. 133 | func GetLoadAvgSample() (samp LoadAvgSample) { 134 | return getLoadAvgSample("/proc/loadavg") 135 | } 136 | 137 | // GetMemSample takes a snapshot of memory info from the /proc/meminfo file. 138 | func GetMemSample() (samp MemSample) { 139 | return getMemSample("/proc/meminfo") 140 | } 141 | -------------------------------------------------------------------------------- /Top.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "io/ioutil" 4 | import "strconv" 5 | import "regexp" 6 | import "errors" 7 | import "time" 8 | import "os" 9 | import "log" 10 | 11 | //process info sructure 12 | type ProcessItem struct { 13 | Pid int 14 | Name string 15 | User string 16 | Cpu float32 17 | Memory int 18 | } 19 | 20 | type Timeval struct { 21 | Sec int32 22 | Usec int32 23 | } 24 | 25 | type Top struct { 26 | processItems []ProcessItem 27 | collectInfoJob BatchJob 28 | lastRequestTime time.Time 29 | } 30 | 31 | func (top *Top) StartCollectInfo() error { 32 | top.collectInfoJob.Job = top.collectInfo 33 | top.lastRequestTime = time.Now() 34 | err := top.collectInfoJob.Start() 35 | if err != nil { 36 | stErr := "error: Can't start collect info job" 37 | log.Println(stErr) 38 | return errors.New(stErr) 39 | } 40 | return nil 41 | } 42 | func (top *Top) StopCollectInfo() error { 43 | err := top.collectInfoJob.Stop() 44 | if err != nil { 45 | stErr := "error: Can't stop collect info job" 46 | log.Println(stErr) 47 | return errors.New(stErr) 48 | } 49 | return nil 50 | } 51 | 52 | func (top *Top) getTicksbyPid(pid int) (int64, error) { 53 | statFileData, err := ioutil.ReadFile("/proc/" + strconv.Itoa(pid) + "/stat") 54 | if err != nil { 55 | stErr := "info: problem with read stat file with pid " + strconv.Itoa(pid) 56 | log.Println(stErr) 57 | return 0, errors.New(stErr) 58 | } 59 | statFileStr := string(statFileData) 60 | cpuTimeReg := regexp.MustCompile("\\d+") 61 | cpuStatField := cpuTimeReg.FindAllString(statFileStr, -1) 62 | utime, _ := strconv.Atoi(cpuStatField[11]) 63 | stime, _ := strconv.Atoi(cpuStatField[12]) 64 | cutime, _ := strconv.Atoi(cpuStatField[13]) 65 | cstime, _ := strconv.Atoi(cpuStatField[14]) 66 | sumTime := int64(utime + stime + cutime + cstime) 67 | return sumTime, nil 68 | } 69 | 70 | func (top *Top) getTicksProcessor() (int64, error) { 71 | statFileData, err := ioutil.ReadFile("/proc/stat") 72 | if err != nil { 73 | stErr := "error: problem with read proc filesystem" 74 | log.Println(stErr) 75 | return 0, errors.New(stErr) 76 | } 77 | statFileStr := string(statFileData) 78 | cpuTimeReg := regexp.MustCompile("\\d+") 79 | cpuStatField := cpuTimeReg.FindAllString(statFileStr, -1)[:9] 80 | var sumTime int64 81 | for _, cpuField := range cpuStatField { 82 | tim, _ := strconv.Atoi(cpuField) 83 | sumTime += int64(tim) 84 | } 85 | return sumTime, nil 86 | } 87 | 88 | func (top *Top) getTicksMap(pids []int) map[int]int64 { 89 | ticksMap := make(map[int]int64) 90 | for _, element := range pids { 91 | ticks, err := top.getTicksbyPid(element) 92 | if err != nil { 93 | ticksMap[element] = 0 94 | } 95 | ticksMap[element] = ticks 96 | } 97 | return ticksMap 98 | } 99 | 100 | func (top *Top) collectInfo() { 101 | for { 102 | pids, err := top.getAllPids() 103 | if err != nil { 104 | stErr := "error: problem with read proc filesystem" 105 | log.Println(stErr) 106 | return //errors.New("error: problem with read proc filesystem") 107 | } 108 | StartTicks := top.getTicksMap(pids) 109 | sumOldTick, _ := top.getTicksProcessor() 110 | time.Sleep(1000 * time.Millisecond) 111 | pids, _ = top.getAllPids() 112 | EndTicks := top.getTicksMap(pids) 113 | sumNewTick, _ := top.getTicksProcessor() 114 | 115 | top.processItems = top.fillProcessInfo(StartTicks, EndTicks, sumNewTick-sumOldTick) 116 | secondLastRequest := time.Now().Sub(top.lastRequestTime) 117 | if secondLastRequest.Seconds() > 5 { 118 | log.Println("info: sleep collect top info job") 119 | top.collectInfoJob.Stop() 120 | } 121 | } 122 | return 123 | } 124 | 125 | // 126 | func (top *Top) getAllPids() ([]int, error) { 127 | dirContent, err := ioutil.ReadDir("/proc/") 128 | if err != nil { 129 | return nil, err 130 | } 131 | var process []int 132 | for _, element := range dirContent { 133 | validPID := regexp.MustCompile(`^[0-9]+$`) 134 | if element.IsDir() && validPID.MatchString(element.Name()) { 135 | pid, _ := strconv.Atoi(element.Name()) 136 | process = append(process, pid) 137 | } 138 | } 139 | return process, nil 140 | } 141 | 142 | func (top *Top) fillProcessInfo(oldTicks map[int]int64, newTicks map[int]int64, sumTicks int64) []ProcessItem { 143 | processItems := []ProcessItem{} 144 | for i, newTickVal := range newTicks { 145 | statFileData, err := ioutil.ReadFile("/proc/" + strconv.Itoa(i) + "/status") 146 | if err == nil { 147 | processItem := ProcessItem{} 148 | processItem.Pid = i 149 | statFileStr := string(statFileData) 150 | regName := regexp.MustCompile("Name:\t(.*)\n") 151 | processItem.Name = regName.FindAllStringSubmatch(statFileStr, -1)[0][1] 152 | regUid := regexp.MustCompile("Uid:\t(\\w+)") 153 | processItem.User = regUid.FindAllStringSubmatch(statFileStr, -1)[0][1] 154 | regMem := regexp.MustCompile("VmRSS:\\s+(\\d+)") 155 | if regMem.MatchString(statFileStr) { 156 | intMem, _ := strconv.Atoi(regMem.FindAllStringSubmatch(statFileStr, -1)[0][1]) 157 | processItem.Memory = intMem 158 | } 159 | oldTickVal, exist := oldTicks[i] 160 | if exist { 161 | 162 | processItem.Cpu = float32(float64(newTickVal-oldTickVal) / float64(sumTicks)) 163 | 164 | } else { 165 | stErr := "info: can't messure cpu info for process" 166 | log.Println(stErr, processItem.Name, processItem.Pid) 167 | processItem.Cpu = 0 168 | } 169 | processItems = append(processItems, processItem) 170 | } 171 | } 172 | return processItems 173 | } 174 | 175 | // 176 | func (top *Top) GetProcessList() ([]ProcessItem, error) { 177 | if !top.collectInfoJob.IsRunning() { 178 | log.Println("info: start collect top info job") 179 | top.collectInfoJob.Start() 180 | } 181 | top.lastRequestTime = time.Now() 182 | return top.processItems, nil 183 | } 184 | 185 | func (top *Top) KillProcess(pid int) error { 186 | process, err := os.FindProcess(pid) 187 | if err != nil { 188 | stErr := "info: can't find process with pid " + strconv.Itoa(pid) 189 | log.Println(stErr) 190 | return errors.New(stErr) 191 | } 192 | err = process.Kill() 193 | if err != nil { 194 | stErr := "info: can't kill process with pid " + strconv.Itoa(pid) 195 | log.Println(stErr) 196 | return errors.New(stErr) 197 | } 198 | return nil 199 | } 200 | -------------------------------------------------------------------------------- /SystemStatLinux.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013 Phillip Bond 2 | // Licensed under the MIT License 3 | // see file LICENSE 4 | 5 | // +build linux 6 | 7 | package main 8 | 9 | import "bufio" 10 | import "bytes" 11 | import "io" 12 | import "io/ioutil" 13 | import "log" 14 | import "runtime" 15 | import "strconv" 16 | import "strings" 17 | import "syscall" 18 | import "time" 19 | 20 | //import "errors" 21 | 22 | func getUptime(procfile string) (uptime UptimeSample) { 23 | // read in whole uptime file with cpu usage information ;"/proc/uptime" 24 | contents, err := ioutil.ReadFile(procfile) 25 | uptime.Time = time.Now() 26 | if err != nil { 27 | return 28 | } 29 | 30 | reader := bufio.NewReader(bytes.NewBuffer(contents)) 31 | line, _, err := reader.ReadLine() 32 | fields := strings.Fields(string(line)) 33 | 34 | val, numerr := strconv.ParseFloat(fields[0], 64) 35 | if numerr != nil { 36 | return 37 | } 38 | uptime.Uptime = val 39 | 40 | return 41 | } 42 | 43 | func getLoadAvgSample(procfile string) (samp LoadAvgSample) { 44 | // read in whole loadavg file with cpu usage information ;"/proc/loadavg" 45 | contents, err := ioutil.ReadFile(procfile) 46 | samp.Time = time.Now() 47 | if err != nil { 48 | return 49 | } 50 | 51 | reader := bufio.NewReader(bytes.NewBuffer(contents)) 52 | line, _, err := reader.ReadLine() 53 | fields := strings.Fields(string(line)) 54 | for i := 0; i < 3; i++ { 55 | val, numerr := strconv.ParseFloat(fields[i], 64) 56 | if numerr != nil { 57 | return 58 | } 59 | switch i { 60 | case 0: 61 | samp.One = val 62 | case 1: 63 | samp.Five = val 64 | case 2: 65 | samp.Fifteen = val 66 | } 67 | } 68 | 69 | return 70 | } 71 | 72 | func getMemSample(procfile string) (samp MemSample) { 73 | want := map[string]bool{ 74 | "Buffers:": true, 75 | "Cached:": true, 76 | "MemTotal:": true, 77 | "MemFree:": true, 78 | "MemUsed:": true, 79 | "SwapTotal:": true, 80 | "SwapFree:": true, 81 | "SwapUsed:": true} 82 | 83 | // read in whole meminfo file with cpu usage information ;"/proc/meminfo" 84 | contents, err := ioutil.ReadFile(procfile) 85 | samp.Time = time.Now() 86 | if err != nil { 87 | return 88 | } 89 | 90 | reader := bufio.NewReader(bytes.NewBuffer(contents)) 91 | for { 92 | line, _, err := reader.ReadLine() 93 | if err == io.EOF { 94 | break 95 | } 96 | 97 | fields := strings.Fields(string(line)) 98 | fieldName := fields[0] 99 | 100 | _, ok := want[fieldName] 101 | if ok && len(fields) == 3 { 102 | val, numerr := strconv.ParseUint(fields[1], 10, 64) 103 | if numerr != nil { 104 | return 105 | } 106 | switch fieldName { 107 | case "Buffers:": 108 | samp.Buffers = val 109 | case "Cached:": 110 | samp.Cached = val 111 | case "MemTotal:": 112 | samp.MemTotal = val 113 | case "MemFree:": 114 | samp.MemFree = val 115 | case "SwapTotal:": 116 | samp.SwapTotal = val 117 | case "SwapFree:": 118 | samp.SwapFree = val 119 | } 120 | } 121 | } 122 | samp.MemUsed = samp.MemTotal - samp.MemFree 123 | samp.SwapUsed = samp.SwapTotal - samp.SwapFree 124 | return 125 | } 126 | 127 | func getProcCPUSample() (s ProcCPUSample) { 128 | var processInfo syscall.Rusage 129 | syscall.Getrusage(syscall.RUSAGE_SELF, &processInfo) 130 | 131 | s.Time = time.Now() 132 | s.ProcMemUsedK = int64(processInfo.Maxrss) 133 | s.User = float64(processInfo.Utime.Usec)/1000000 + float64(processInfo.Utime.Sec) 134 | s.System = float64(processInfo.Stime.Usec)/1000000 + float64(processInfo.Stime.Sec) 135 | s.Total = s.User + s.System 136 | 137 | return 138 | } 139 | 140 | func getCPUSample(procfile string) (samp CPUSample) { 141 | // read in whole proc file with cpu usage information ; "/proc/stat" 142 | contents, err := ioutil.ReadFile(procfile) 143 | samp.Time = time.Now() 144 | if err != nil { 145 | return 146 | } 147 | 148 | reader := bufio.NewReader(bytes.NewBuffer(contents)) 149 | for { 150 | line, _, err := reader.ReadLine() 151 | if err == io.EOF { 152 | break 153 | } 154 | 155 | fields := strings.Fields(string(line)) 156 | 157 | if len(fields) > 0 { 158 | fieldName := fields[0] 159 | if fieldName == "cpu" { 160 | parseCPUFields(fields, &samp) 161 | } 162 | } 163 | } 164 | return 165 | } 166 | 167 | func getSimpleCPUAverage(first CPUSample, second CPUSample) (avg SimpleCPUAverage) { 168 | //walltimediff := second.Time.Sub(first.Time) 169 | //dT := float64(first.Total - second.Total) 170 | 171 | dI := float64(second.Idle - first.Idle) 172 | dTot := float64(second.Total - first.Total) 173 | avg.IdlePct = dI / dTot * 100 174 | avg.BusyPct = (dTot - dI) * 100 / dTot 175 | //log.Printf("cpu idle ticks %f, total ticks %f, idle pct %f, busy pct %f\n", dI, dTot, avg.IdlePct, avg.BusyPct) 176 | return 177 | } 178 | 179 | func subtractAndConvertTicks(first uint64, second uint64) float64 { 180 | return float64(first - second) 181 | } 182 | 183 | func getCPUAverage(first CPUSample, second CPUSample) (avg CPUAverage) { 184 | dTot := float64(second.Total - first.Total) 185 | invQuotient := 100.00 / dTot 186 | 187 | avg.UserPct = subtractAndConvertTicks(second.User, first.User) * invQuotient 188 | avg.NicePct = subtractAndConvertTicks(second.Nice, first.Nice) * invQuotient 189 | avg.SystemPct = subtractAndConvertTicks(second.System, first.System) * invQuotient 190 | avg.IdlePct = subtractAndConvertTicks(second.Idle, first.Idle) * invQuotient 191 | avg.IowaitPct = subtractAndConvertTicks(second.Iowait, first.Iowait) * invQuotient 192 | avg.IrqPct = subtractAndConvertTicks(second.Irq, first.Irq) * invQuotient 193 | avg.SoftIrqPct = subtractAndConvertTicks(second.SoftIrq, first.SoftIrq) * invQuotient 194 | avg.StealPct = subtractAndConvertTicks(second.Steal, first.Steal) * invQuotient 195 | avg.GuestPct = subtractAndConvertTicks(second.Guest, first.Guest) * invQuotient 196 | avg.Time = second.Time 197 | avg.Seconds = second.Time.Sub(first.Time).Seconds() 198 | return 199 | } 200 | 201 | func getProcCPUAverage(first ProcCPUSample, second ProcCPUSample, procUptime float64) (avg ProcCPUAverage) { 202 | dT := second.Time.Sub(first.Time).Seconds() 203 | 204 | avg.UserPct = 100 * (second.User - first.User) / dT 205 | avg.SystemPct = 100 * (second.System - first.System) / dT 206 | avg.TotalPct = 100 * (second.Total - first.Total) / dT 207 | avg.PossiblePct = 100.0 * float64(runtime.NumCPU()) 208 | avg.CumulativeTotalPct = 100 * second.Total / procUptime 209 | avg.Time = second.Time 210 | avg.Seconds = dT 211 | return 212 | } 213 | 214 | func parseCPUFields(fields []string, stat *CPUSample) { 215 | numFields := len(fields) 216 | stat.Name = fields[0] 217 | for i := 1; i < numFields; i++ { 218 | val, numerr := strconv.ParseUint(fields[i], 10, 64) 219 | if numerr != nil { 220 | log.Println("systemstat.parseCPUFields(): Error parsing (field, value): ", i, fields[i]) 221 | } 222 | stat.Total += val 223 | switch i { 224 | case 1: 225 | stat.User = val 226 | case 2: 227 | stat.Nice = val 228 | case 3: 229 | stat.System = val 230 | case 4: 231 | stat.Idle = val 232 | case 5: 233 | stat.Iowait = val 234 | case 6: 235 | stat.Irq = val 236 | case 7: 237 | stat.SoftIrq = val 238 | case 8: 239 | stat.Steal = val 240 | case 9: 241 | stat.Guest = val 242 | } 243 | } 244 | } 245 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 |
5 |User:10%
292 |System:10%
293 |Idle:10%
294 |Guest:10%
295 |Low:10%
296 |Memory used:10%
299 |Memory free:10%
300 |Buffers:10%
301 |Cached:10%
302 |Memory total:10%
303 || Pid | 312 |Process name | 313 |User id | 314 |Cpu >=0% | 315 |Mem Mb. | 316 |
|---|