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 = 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)
|
||||||
|
|
26
adb_shell.go
26
adb_shell.go
|
@ -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)
|
|
||||||
}
|
|
||||||
|
|
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