-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit a0095d8
Showing
17 changed files
with
1,077 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
name: CI | ||
|
||
on: | ||
push: | ||
branches: [ master ] | ||
pull_request: | ||
branches: [ master ] | ||
|
||
jobs: | ||
testandinstall: | ||
runs-on: ubuntu-latest | ||
|
||
steps: | ||
- uses: actions/checkout@v2 | ||
|
||
- name: Ensure all files were formatted as per gofmt | ||
run: | | ||
gofmt -l $(find . -name '*.go') >/dev/null | ||
- name: run tests | ||
run: go test ./... | ||
|
||
- name: install binaries | ||
run: go install github.com/gokrazy/stat/cmd/... |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
Copyright (c) 2017 the gokrazy authors. All rights reserved. | ||
|
||
Redistribution and use in source and binary forms, with or without | ||
modification, are permitted provided that the following conditions are | ||
met: | ||
|
||
* Redistributions of source code must retain the above copyright | ||
notice, this list of conditions and the following disclaimer. | ||
* Redistributions in binary form must reproduce the above | ||
copyright notice, this list of conditions and the following disclaimer | ||
in the documentation and/or other materials provided with the | ||
distribution. | ||
* Neither the name of gokrazy nor the names of its | ||
contributors may be used to endorse or promote products derived from | ||
this software without specific prior written permission. | ||
|
||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | ||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | ||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | ||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | ||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | ||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
# gokrazy/stat | ||
|
||
`gokrazy/stat` is a program to visualize resource usage, producing output that | ||
looks very similar to [Dag Wieers’s | ||
`dstat`](https://github.com/dstat-real/dstat) default output. | ||
|
||
As the repository path implies, this program is meant to be used on the | ||
https://gokrazy.org/ Raspberry Pi platform, so it does not have any external | ||
runtime dependencies and is implemented natively in Go (not using any cgo). The | ||
resulting static binary might come in handy in other Linux environments, too! | ||
|
||
|
||
## github.com/gokrazy/stat/cmd/gokr-webstat | ||
|
||
`gokr-webstat` displays system resource usage in your browser, with output | ||
matching `gokr-stat` or `dstat`! | ||
|
||
It uses the [EventSource | ||
API](https://developer.mozilla.org/en-US/docs/Web/API/EventSource) to stream new | ||
lines into your browser. | ||
|
||
![webstat screenshot](2020-06-27-midna-webstat.jpg) | ||
|
||
## github.com/gokrazy/stat/cmd/gokr-stat | ||
|
||
The `gokr-stat` program is a terminal program that displays system resource | ||
usage, like `dstat`. | ||
|
||
Here is the `gokr-stat` terminal output (via | ||
[`gokrazy/breakglass`](https://github.com/gokrazy/breakglass)) when scanning one | ||
double-sided piece of A4 paper with | ||
[scan2drive](https://github.com/stapelberg/scan2drive) on a Raspberry Pi 4: | ||
|
||
![stat terminal output](2020-06-27-scan2drive-scan-gokrazy-stat.jpg) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,167 @@ | ||
package main | ||
|
||
import ( | ||
"flag" | ||
"fmt" | ||
"io" | ||
"io/ioutil" | ||
"log" | ||
"os" | ||
"os/signal" | ||
"strings" | ||
"sync" | ||
"syscall" | ||
"time" | ||
"unsafe" | ||
|
||
"github.com/gokrazy/stat" | ||
"github.com/gokrazy/stat/internal/cpu" | ||
"github.com/gokrazy/stat/internal/disk" | ||
"github.com/gokrazy/stat/internal/mem" | ||
"github.com/gokrazy/stat/internal/net" | ||
"github.com/gokrazy/stat/internal/sys" | ||
) | ||
|
||
func formatCols(cols []stat.Col) string { | ||
formatted := make([]string, len(cols)) | ||
for idx, col := range cols { | ||
formatted[idx] = col.String() | ||
} | ||
return strings.Join(formatted, " ") | ||
} | ||
|
||
const ( | ||
TIOCGWINSZ = 0x5413 | ||
) | ||
|
||
type window struct { | ||
Row uint16 | ||
Col uint16 | ||
Xpixel uint16 | ||
Ypixel uint16 | ||
} | ||
|
||
func terminalSize() (*window, error) { | ||
w := new(window) | ||
tio := syscall.TIOCGWINSZ | ||
res, _, err := syscall.Syscall(syscall.SYS_IOCTL, | ||
uintptr(syscall.Stdin), | ||
uintptr(tio), | ||
uintptr(unsafe.Pointer(w)), | ||
) | ||
if int(res) == -1 { | ||
if err == syscall.ENOTTY { | ||
return &window{Row: 80}, nil | ||
} | ||
return nil, err | ||
} | ||
return w, nil | ||
} | ||
|
||
func printStats() error { | ||
flag.Parse() | ||
|
||
ts, err := terminalSize() | ||
if err != nil { | ||
return err | ||
} | ||
var rowsMu sync.Mutex | ||
rows := int(ts.Row) - 1 | ||
ch := make(chan os.Signal, 1) | ||
signal.Notify(ch, syscall.SIGWINCH) | ||
go func() { | ||
for range ch { | ||
ts, err := terminalSize() | ||
if err != nil { | ||
log.Print(err) | ||
continue | ||
} | ||
rowsMu.Lock() | ||
rows = int(ts.Row) - 1 | ||
rowsMu.Unlock() | ||
} | ||
}() | ||
|
||
type processAndFormatter interface { | ||
ProcessAndFormat(map[string][]byte) []stat.Col | ||
} | ||
modules := []processAndFormatter{ | ||
&cpu.Stats{}, | ||
&disk.Stats{}, | ||
&sys.Stats{}, | ||
&net.Stats{}, | ||
&mem.Stats{}, | ||
} | ||
header := func() { | ||
const blue = "\033[1;34m" | ||
fmt.Printf(blue + "usr sys idl wai stl | ") | ||
fmt.Printf("_read _writ | ") | ||
fmt.Printf("_int_ _csw_ | ") | ||
fmt.Printf("_recv _send | ") | ||
fmt.Printf("_used _free _buff _cach\n") | ||
} | ||
parts := make([]string, len(modules)) | ||
files := make(map[string]*os.File) | ||
for _, mod := range modules { | ||
// When a stats module implements the FileContents() interface, we | ||
// ensure all returned file contents are read and passed to | ||
// ProcessAndFormat. | ||
fc, ok := mod.(interface{ FileContents() []string }) | ||
if !ok { | ||
continue | ||
} | ||
for _, f := range fc.FileContents() { | ||
if _, ok := files[f]; ok { | ||
continue // already requested | ||
} | ||
fl, err := os.Open(f) | ||
if err != nil { | ||
return err | ||
} | ||
files[f] = fl | ||
} | ||
} | ||
for i := 0; ; i++ { | ||
rowsMu.Lock() | ||
showHeader := i%(int(rows)-1) == 0 | ||
rowsMu.Unlock() | ||
if showHeader { | ||
header() | ||
} | ||
contents := make(map[string][]byte) | ||
for path, fl := range files { | ||
if _, err := fl.Seek(0, io.SeekStart); err != nil { | ||
return err | ||
} | ||
b, err := ioutil.ReadAll(fl) | ||
if err != nil { | ||
return err | ||
} | ||
contents[path] = b | ||
} | ||
|
||
for idx, mod := range modules { | ||
parts[idx] = formatCols(mod.ProcessAndFormat(contents)) | ||
} | ||
|
||
if i > 0 { | ||
const darkblue = "\033[0;34m" | ||
fmt.Println(strings.Join(parts, darkblue+" | ")) | ||
// TODO: clear colors at the end of line so that program can be interrupted | ||
} | ||
|
||
time.Sleep(1 * time.Second) | ||
} | ||
} | ||
|
||
func main() { | ||
if os.Getenv("GOKRAZY_FIRST_START") == "1" { | ||
// Do not supervise this process: it is meant for interactive usage on a | ||
// terminal. If you are looking for a daemon, use gokr-webstat instead. | ||
os.Exit(125) | ||
} | ||
|
||
if err := printStats(); err != nil { | ||
log.Fatal(err) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
package main | ||
|
||
const statusTmpl = `<!DOCTYPE html> | ||
<html> | ||
<head> | ||
<title>{{ .Hostname }} - webstat</title> | ||
<style type="text/css"> | ||
body { | ||
background-color: #eee; | ||
} | ||
#readings { | ||
background-color: black; | ||
border: 1px solid grey; | ||
font-family: monospace; | ||
} | ||
th { | ||
color: white; | ||
text-align: left; | ||
} | ||
</style> | ||
</head> | ||
<body> | ||
<h1>{{ .Hostname }} - webstat</h1> | ||
<table id="readings"> | ||
<thead> | ||
<tr> | ||
{{ range $idx, $val := .Headers }} | ||
<th>{{ $val }}</th> | ||
{{ end }} | ||
</tr> | ||
</thead> | ||
<tbody> | ||
<tr><td> </td></tr> | ||
<tr><td> </td></tr> | ||
<tr><td> </td></tr> | ||
<tr><td> </td></tr> | ||
<tr><td> </td></tr> | ||
<tr><td> </td></tr> | ||
<tr><td> </td></tr> | ||
<tr><td> </td></tr> | ||
<tr><td> </td></tr> | ||
<tr><td> </td></tr> | ||
<tr><td> </td></tr> | ||
<tr><td> </td></tr> | ||
<tr><td> </td></tr> | ||
<tr><td> </td></tr> | ||
<tr><td> </td></tr> | ||
<tr><td> </td></tr> | ||
<tr><td> </td></tr> | ||
<tr><td> </td></tr> | ||
<tr><td> </td></tr> | ||
<tr><td> </td></tr> | ||
</tbody> | ||
</table> | ||
<script type="text/javascript"> | ||
var readingstbody = document.getElementById('readings').children[1]; | ||
var readings = new EventSource('/readings'); | ||
readings.onmessage = function(e) { | ||
var parts = JSON.parse(e.data); | ||
var innerHTML = ''; | ||
for (part of parts) { | ||
innerHTML += part; | ||
} | ||
for (var i = 0; i < 19; i++) { | ||
// copy from i+1 to i, overwriting the oldest entry | ||
readingstbody.children[i].innerHTML = readingstbody.children[i+1].innerHTML | ||
} | ||
readingstbody.children[19].innerHTML = innerHTML; | ||
} | ||
</script> | ||
</body> | ||
</html>` |
Oops, something went wrong.