Update README and some fixes

This commit is contained in:
Ivan Danyliuk 2017-12-16 15:20:26 +01:00
parent bc7d4f5890
commit eff38714c9
No known key found for this signature in database
GPG Key ID: 97ED33CE024E1DBF
6 changed files with 88 additions and 23 deletions

View File

@ -1,15 +1,53 @@
# StatusMonitor
Simple command line app for monitoring CPU of the Status.im application running on the device (or simulator).
---
TODO:
- iOS support
- Add more metrics (memory, custom expvars, etc)
- Optimize for higher frequencies (now 1s resolution)
---
# Installation
Just:
```
go get github.com/divan/statusmonitor
```
# Usage
Just run `statusmonitor` binary. It will automatically connect to your device using `adb`, find the Status.im PID and start collecting data:
```
./statusmonitor
```
![](./demo/demo0.png)
Press `q` to exit.
If you want to analyze data after, use `-csv` switch — all data will be written in the CSV file. Name of the file is autogenerated in form `20160102_150405.csv` in the current folder.
# Plot CSV with R
To analyze the data, you may use R lang. Here is a sample code that works well in R Studio:
Load data and convert UNIX timestamps into R's POSIXct object:
``` ```
df = read.csv("~/Downloads/data.csv") df = read.csv("~/Downloads/data.csv")
df$timestamp = as.POSIXct(df$timestamp, origin="1970-01-01") df$timestamp = as.POSIXct(df$timestamp, origin="1970-01-01")
``` ```
Draw the plot; Draw the plot:
``` ```
plot(df, type="l", main="status CPU usage", col = 'green', lwd=2, cex.main=1.5, cex.axis=1, xlab="time", xaxt="n") plot(df, type="l", main="status CPU usage", col = 'green', lwd=2, cex.main=1.5, cex.axis=1, xlab="time", xaxt="n")
axis.POSIXct(1, df$timestamp, format="%H:%M:%S") axis.POSIXct(1, df$timestamp, format="%H:%M:%S")
``` ```
![](./demo/demo1.png)
or interactive version with plotly: or interactive version with plotly:
@ -17,3 +55,4 @@ or interactive version with plotly:
library(plotly) library(plotly)
plot_ly(df, x = df$timestamp, y = df$cpu, type = 'scatter', mode = 'lines', fill = 'tozeroy') plot_ly(df, x = df$timestamp, y = df$cpu, type = 'scatter', mode = 'lines', fill = 'tozeroy')
``` ```
![](./demo/demo2.png)

View File

@ -5,7 +5,6 @@ import (
"errors" "errors"
"fmt" "fmt"
"os/exec" "os/exec"
"strconv"
"strings" "strings"
) )
@ -22,22 +21,12 @@ func adbCPU(pid int64) (float64, error) {
return 0, err return 0, err
} }
line := parseTopOutput(out) top, err := NewTopOutput(out)
fields := strings.Fields(line)
if len(fields) < 10 {
fmt.Println("[ERROR]: wrong top output", fields)
return 0, ErrParse
}
cpu, err := strconv.ParseFloat(fields[9], 64)
if err != nil { if err != nil {
// this usually means that app is in background and top return 0, err
// omits
fmt.Println("[ERROR] Parse CPU value:", err)
fmt.Println("Output:", fields)
return 0, ErrParse
} }
return cpu, nil
return top.CPU, nil
} }
// adbShell calls custom command via 'adb shell` and returns it's stdout output. // adbShell calls custom command via 'adb shell` and returns it's stdout output.
@ -59,10 +48,3 @@ func adbShell(command string) (string, error) {
return buf.String(), nil return buf.String(), nil
} }
// parseTopOutput parses line from the android shell top's output.
// It contains one line with data and many newlines.
func parseTopOutput(data string) string {
lines := strings.Split(data, "\r")
return strings.Replace(lines[0], "\r", "", -1)
}

BIN
demo/demo0.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 72 KiB

BIN
demo/demo1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 98 KiB

BIN
demo/demo2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 109 KiB

44
top.go Normal file
View File

@ -0,0 +1,44 @@
package main
import (
"fmt"
"strconv"
"strings"
)
// TopOutput represents data from 'top' command output
// for single process.
type TopOutput struct {
CPU float64
}
// NewTopOutput creates new TopOutput from raw stdout data.
func NewTopOutput(data string) (*TopOutput, error) {
fields := parseTopOutput(data)
if len(fields) < 11 {
fmt.Println("[ERROR]: wrong top output", fields)
return nil, ErrParse
}
// eithh field is supposed to be CPU value
cpu, err := strconv.ParseFloat(fields[8], 64)
if err != nil {
// top output might be different from system to system, so log
// this verbosely
fmt.Println("[ERROR] Parse CPU value:", err)
fmt.Println("Output:", fields)
return nil, ErrParse
}
return &TopOutput{
CPU: cpu,
}, nil
}
func parseTopOutput(data string) []string {
lines := strings.Split(data, "\r")
line := strings.Replace(lines[0], "\r", "", -1)
line = strings.TrimSpace(line)
fields := strings.Fields(line)
return fields
}