Update README and some fixes
This commit is contained in:
parent
bc7d4f5890
commit
eff38714c9
41
README.md
41
README.md
|
@ -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$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")
|
||||
axis.POSIXct(1, df$timestamp, format="%H:%M:%S")
|
||||
```
|
||||
![](./demo/demo1.png)
|
||||
|
||||
or interactive version with plotly:
|
||||
|
||||
|
@ -17,3 +55,4 @@ or interactive version with plotly:
|
|||
library(plotly)
|
||||
plot_ly(df, x = df$timestamp, y = df$cpu, type = 'scatter', mode = 'lines', fill = 'tozeroy')
|
||||
```
|
||||
![](./demo/demo2.png)
|
||||
|
|
26
adb_shell.go
26
adb_shell.go
|
@ -5,7 +5,6 @@ import (
|
|||
"errors"
|
||||
"fmt"
|
||||
"os/exec"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
|
@ -22,22 +21,12 @@ func adbCPU(pid int64) (float64, error) {
|
|||
return 0, err
|
||||
}
|
||||
|
||||
line := parseTopOutput(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)
|
||||
top, err := NewTopOutput(out)
|
||||
if err != nil {
|
||||
// this usually means that app is in background and top
|
||||
// omits
|
||||
fmt.Println("[ERROR] Parse CPU value:", err)
|
||||
fmt.Println("Output:", fields)
|
||||
return 0, ErrParse
|
||||
return 0, err
|
||||
}
|
||||
return cpu, nil
|
||||
|
||||
return top.CPU, nil
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
// 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)
|
||||
}
|
||||
|
|
Binary file not shown.
After Width: | Height: | Size: 72 KiB |
Binary file not shown.
After Width: | Height: | Size: 98 KiB |
Binary file not shown.
After Width: | Height: | Size: 109 KiB |
|
@ -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
|
||||
}
|
Loading…
Reference in New Issue