-
Notifications
You must be signed in to change notification settings - Fork 117
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
1 parent
e39ec2f
commit 3edbecd
Showing
27 changed files
with
1,484 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,18 @@ | ||
*.ipr | ||
*.iml | ||
*.jar | ||
*.class | ||
*.iws | ||
rice-box.go | ||
# output and ide dirs | ||
build/ | ||
target/ | ||
out/ | ||
.gradle/ | ||
.vscode/ | ||
vendor/ | ||
.idea/ | ||
|
||
# bin | ||
jvm-mon-go | ||
log |
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,39 @@ | ||
# jvm-mon | ||
|
||
JVM monitoring from the terminal (cpu, memory, threads). | ||
Single executable written in Go and Java. | ||
Supports monitoring application running on Java 8 and newer. | ||
|
||
# Build | ||
|
||
Prerequisites: | ||
- Go (at least 1.13) | ||
- https://github.com/Masterminds/glide for dependency management. | ||
- https://github.com/GeertJohan/go.rice for embedded files | ||
- JDK 8+ for building java agent | ||
|
||
1. Build java agent: `./gradlew jar` | ||
2. Install Go dependencies: | ||
``` | ||
glide update | ||
glide install | ||
``` | ||
3. `go build` | ||
|
||
# Usage | ||
|
||
To monitor JVMs started with your username: | ||
|
||
`./jvm-mon-go` | ||
|
||
# How it works | ||
|
||
jvm-mon attaches to a running JVM you select and loads an agent.jar into the process. | ||
They communicate via a socket to send/receive JVM metrics in json format. | ||
|
||
# Development | ||
|
||
Run jvm-mon from Go sources: `./run.sh` | ||
Run a java process: `./agent.sh` | ||
|
||
See `log` file for debugging output |
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,23 @@ | ||
apply plugin: 'java' | ||
|
||
sourceCompatibility = 1.8 | ||
targetCompatibility = 1.8 | ||
|
||
repositories { | ||
mavenCentral() | ||
} | ||
|
||
jar { | ||
manifest { | ||
attributes "Main-Class": "jvmmon.Agent" | ||
attributes "Agent-Class": "jvmmon.Agent" | ||
attributes "Premain-Class": "jvmmon.Agent" | ||
attributes "Can-Redefine-Classes": 'false' | ||
attributes "Can-Retransform-Classes": 'false' | ||
attributes 'Can-Set-Native-Method-Prefix': 'false' | ||
} | ||
} | ||
|
||
dependencies { | ||
testCompile 'junit:junit:4.12' | ||
} |
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 @@ | ||
#!/bin/bash | ||
|
||
./make-agent.sh | ||
|
||
echo "Building" | ||
go build -o build/ | ||
|
||
export GOOS=linux | ||
export GOARCH=amd64 | ||
|
||
DIR="${GOOS}_${GOARCH}" | ||
rm build/${DIR}/* | ||
mkdir -p build/${DIR} | ||
|
||
echo "Building $DIR" | ||
go build -o build/${DIR} | ||
|
||
rm build/*.tgz | ||
tar cvzf build/jvm-mon-osx.tgz -C build jvm-mon-go | ||
tar cvzf build/jvm-mon-linux64.tgz -C build/linux_amd64 jvm-mon-go | ||
|
||
#Mac: | ||
#GOOS=darwin | ||
#GOARCH=amd64 |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
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,10 @@ | ||
package: . | ||
import: | ||
- package: github.com/gizak/termui | ||
version: v2.2.0 | ||
- package: github.com/maruel/panicparse | ||
version: v1.0.2 | ||
- package: github.com/nsf/termbox-go | ||
- package: github.com/mattn/go-runewidth | ||
- package: github.com/GeertJohan/go.rice | ||
version: v1.0.0 |
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,13 @@ | ||
module github.com/ajermakovics/jvm-mon-go | ||
|
||
go 1.13 | ||
|
||
require ( | ||
github.com/GeertJohan/go.rice v1.0.0 | ||
github.com/gizak/termui v2.2.0+incompatible | ||
github.com/maruel/panicparse v1.0.2 | ||
github.com/mattn/go-runewidth v0.0.3-0.20180304235428-a9d6d1e4dc51 | ||
github.com/mitchellh/go-wordwrap v0.0.0-20150314170334-ad45545899c7 | ||
github.com/nsf/termbox-go v0.0.0-20180303152453-e2050e41c884 | ||
github.com/tokuhirom/go-hsperfdata v1.0.4-0.20161127080129-b58598ac84ee | ||
) |
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,25 @@ | ||
github.com/GeertJohan/go.incremental v1.0.0/go.mod h1:6fAjUhbVuX1KcMD3c8TEgVUqmo4seqhv0i0kdATSkM0= | ||
github.com/GeertJohan/go.rice v1.0.0 h1:KkI6O9uMaQU3VEKaj01ulavtF7o1fWT7+pk/4voiMLQ= | ||
github.com/GeertJohan/go.rice v1.0.0/go.mod h1:eH6gbSOAUv07dQuZVnBmoDP8mgsM1rtixis4Tib9if0= | ||
github.com/akavel/rsrc v0.8.0/go.mod h1:uLoCtb9J+EyAqh+26kdrTgmzRBFPGOolLWKpdxkKq+c= | ||
github.com/daaku/go.zipexe v1.0.0 h1:VSOgZtH418pH9L16hC/JrgSNJbbAL26pj7lmD1+CGdY= | ||
github.com/daaku/go.zipexe v1.0.0/go.mod h1:z8IiR6TsVLEYKwXAoE/I+8ys/sDkgTzSL0CLnGVd57E= | ||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= | ||
github.com/gizak/termui v2.2.0+incompatible h1:qvZU9Xll/Xd/Xr/YO+HfBKXhy8a8/94ao6vV9DSXzUE= | ||
github.com/gizak/termui v2.2.0+incompatible/go.mod h1:PkJoWUt/zacQKysNfQtcw1RW+eK2SxkieVBtl+4ovLA= | ||
github.com/go-bindata/go-bindata v3.1.2+incompatible h1:5vjJMVhowQdPzjE1LdxyFF7YFTXg5IgGVW4gBr5IbvE= | ||
github.com/go-bindata/go-bindata v3.1.2+incompatible/go.mod h1:xK8Dsgwmeed+BBsSy2XTopBn/8uK2HWuGSnA11C3Joo= | ||
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= | ||
github.com/maruel/panicparse v1.0.2 h1:dTqB9K2WSvu6JoCQSqSJhNgnir3AA64OHX+jNp4kFNg= | ||
github.com/maruel/panicparse v1.0.2/go.mod h1:nty42YY5QByNC5MM7q/nj938VbgPU7avs45z6NClpxI= | ||
github.com/mattn/go-runewidth v0.0.3-0.20180304235428-a9d6d1e4dc51 h1:73DyaxlfqHF9FQireGHkut6r7OuuA0jTVjS9N0vwktI= | ||
github.com/mattn/go-runewidth v0.0.3-0.20180304235428-a9d6d1e4dc51/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= | ||
github.com/mitchellh/go-wordwrap v0.0.0-20150314170334-ad45545899c7 h1:DpOJ2HYzCv8LZP15IdmG+YdwD2luVPHITV96TkirNBM= | ||
github.com/mitchellh/go-wordwrap v0.0.0-20150314170334-ad45545899c7/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo= | ||
github.com/nkovacs/streamquote v0.0.0-20170412213628-49af9bddb229/go.mod h1:0aYXnNPJ8l7uZxf45rWW1a/uME32OF0rhiYGNQ2oF2E= | ||
github.com/nsf/termbox-go v0.0.0-20180303152453-e2050e41c884 h1:fcs71SMqqDhUD+PbpIv9xf3EH9F9s6HfiLwr6jKm1VA= | ||
github.com/nsf/termbox-go v0.0.0-20180303152453-e2050e41c884/go.mod h1:IuKpRQcYE1Tfu+oAQqaLisqDeXgjyyltCfsaoYN18NQ= | ||
github.com/tokuhirom/go-hsperfdata v1.0.4-0.20161127080129-b58598ac84ee h1:0MtzuMhW7HpwcXokCdJ0Z9OU0Y7JAPMBYbgG4jfpxxw= | ||
github.com/tokuhirom/go-hsperfdata v1.0.4-0.20161127080129-b58598ac84ee/go.mod h1:1qHqOCR9yTTU8uUZxhV7TSaCYszgo85Vb4IU9TqfH6M= | ||
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= | ||
github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8= |
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,176 @@ | ||
package main | ||
|
||
import ( | ||
"encoding/json" | ||
"fmt" | ||
"github.com/GeertJohan/go.rice" | ||
"github.com/GeertJohan/go.rice/embedded" | ||
. "github.com/ajermakovics/jvm-mon-go/jvmmon" | ||
ui "github.com/gizak/termui" | ||
"io/ioutil" | ||
"log" | ||
"os" | ||
"path/filepath" | ||
"strconv" | ||
) | ||
|
||
var pid, jar, port string | ||
var jvms map[string]JVM | ||
var server *Server | ||
var version = "1.0-ea1" | ||
var logFile *os.File | ||
|
||
func init() { | ||
user := GetCurUser() | ||
var logErr error | ||
logPath := os.TempDir() + string(os.PathSeparator) + "jvm-mon_" + user + ".log" | ||
logFile, logErr := os.OpenFile(logPath, os.O_RDWR|os.O_CREATE|os.O_APPEND|os.O_TRUNC, 0666) | ||
if logErr != nil { | ||
log.Fatalf("Error opening %v file: %v", logPath, logErr) | ||
panic(logErr) | ||
} | ||
log.SetOutput(logFile) | ||
println("jvm-mon v:", version, " user:", user, " log:", logPath) | ||
|
||
jvms = GetJVMs() | ||
log.Println("jvm-mon v", version) | ||
log.Println("Found JVMs: ", len(jvms)) | ||
|
||
err := ui.Init() | ||
if err != nil { | ||
log.Fatal("Cannot initialize UI", err) | ||
panic(err) | ||
} | ||
|
||
server, err = NewServer() | ||
if err != nil { | ||
panic(err) | ||
} | ||
port = strconv.Itoa((*server).Port) | ||
go receiveMetrics() | ||
go checkConnections() | ||
} | ||
|
||
func main() { | ||
jar = loadJar() | ||
|
||
// Create UI | ||
jvmTable := NewNavTable(jvms, "JVMs (v" + version + ")", 9) | ||
memChart := NewMemChart() | ||
cpuChart := NewCpuChart() | ||
threadTable := NewThreadTable(9) | ||
|
||
ui.Body.AddRows( | ||
ui.NewRow( | ||
ui.NewCol(6, 0, jvmTable), | ||
ui.NewCol(6, 0, memChart)), | ||
ui.NewRow( | ||
ui.NewCol(6, 0, threadTable), | ||
ui.NewCol(6, 0, cpuChart))) | ||
|
||
ui.Body.Align() | ||
ui.Render(ui.Body) | ||
|
||
ui.Handle("/sys/kbd/C-c", func(ui.Event) { ui.StopLoop() }) | ||
ui.Handle("/sys/kbd/q", func(ui.Event) { ui.StopLoop() }) | ||
ui.Handle("/nav-table/selected", monitor) | ||
|
||
ui.Loop() | ||
ui.Close() | ||
cleanUp() | ||
} | ||
|
||
func cleanUp() { | ||
os.Remove(jar) // from temp | ||
} | ||
|
||
func loadJar() string { | ||
for boxName, _ := range embedded.EmbeddedBoxes { | ||
log.Println("Embedded dir: ", boxName) | ||
} | ||
|
||
box := rice.MustFindBox(`build/libs`) | ||
jarFile, err := box.Open(`jvm-mon-go.jar`) | ||
if err != nil { | ||
panic(err) | ||
} | ||
stat, _ := jarFile.Stat() | ||
log.Println("Found embeded jar file: ", stat.Size()) | ||
jarBytes, err := box.Bytes("jvm-mon-go.jar") | ||
if err != nil { | ||
panic(err) | ||
} | ||
tmpJarFile, err := ioutil.TempFile(os.TempDir(), "jvm-mon-go.jar") | ||
if _, err = tmpJarFile.Write(jarBytes); err != nil { | ||
fmt.Println("Failed to write to temporary file", err) | ||
} | ||
var tmpJarPath = tmpJarFile.Name() | ||
log.Println("Created temp file ", tmpJarPath) | ||
|
||
if err := tmpJarFile.Close(); err != nil { | ||
fmt.Println(err) | ||
} | ||
|
||
err = os.Chmod(tmpJarPath, 0644) | ||
if err != nil { | ||
log.Println("Cannot chmod ", tmpJarPath, " ", err) | ||
} | ||
|
||
return tmpJarPath | ||
} | ||
|
||
func checkConnections() { | ||
for { | ||
addr := <-(*server).Connections | ||
log.Println("JVM Connected ", addr) | ||
} | ||
} | ||
|
||
func receiveMetrics() { | ||
for { | ||
msg := <-(*server).Messages | ||
var metrics Metrics | ||
msgBytes := []byte(msg) | ||
err := json.Unmarshal(msgBytes, &metrics) | ||
if err != nil { | ||
log.Fatal("Cannot unmarshal: ", msg, "err: ", err) | ||
continue | ||
} | ||
|
||
ui.SendCustomEvt("/metrics/mem", metrics) | ||
ui.SendCustomEvt("/metrics/cpu", metrics) | ||
ui.SendCustomEvt("/metrics/threads", metrics.Threads) | ||
} | ||
} | ||
|
||
func monitor(e ui.Event) { | ||
pid = e.Data.(string) | ||
jvm := jvms[pid] | ||
ui.SendCustomEvt("/metrics/mem/clear", pid) | ||
ui.SendCustomEvt("/metrics/cpu/clear", pid) | ||
go attachAgent(jvm, jar, port) | ||
} | ||
|
||
func attachAgent(jvm JVM, jar string, port string) { | ||
err := jvm.AttachAndLoadAgent(jar, port) | ||
if err != nil { | ||
log.Println("Cannot attach to pid ", pid) | ||
} | ||
} | ||
|
||
func findJar() string { | ||
//workDir, _ := os.Getwd() | ||
self, _ := os.Executable() | ||
selfDir := filepath.Dir(self) | ||
|
||
jar = filepath.Join(selfDir, "libs", "jvm-mon-go.jar") // during dev | ||
if _, err := os.Stat(jar); os.IsNotExist(err) { | ||
jar = filepath.Join(selfDir, "jvm-mon-go.jar") | ||
} | ||
if _, err := os.Stat(jar); os.IsNotExist(err) { | ||
log.Fatal("Agent jar not found: ", jar, "Error: ", err) | ||
panic(err) | ||
} | ||
log.Println("Agent jar file: ", jar) | ||
return jar | ||
} |
Oops, something went wrong.