497 lines
16 KiB
Go
497 lines
16 KiB
Go
/*
|
|
Copyright 2021 Alibaba Group Holding Limited.
|
|
|
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
you may not use this file except in compliance with the License.
|
|
You may obtain a copy of the License at
|
|
|
|
http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
Unless required by applicable law or agreed to in writing, software
|
|
distributed under the License is distributed on an "AS IS" BASIS,
|
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
See the License for the specific language governing permissions and
|
|
limitations under the License.
|
|
*/
|
|
|
|
package jvm
|
|
|
|
import (
|
|
"fmt"
|
|
"reflect"
|
|
"strings"
|
|
)
|
|
|
|
const (
|
|
hotspotHsPerfDataJavaThreadsPrefix = "java.threads"
|
|
hotspotHsPerfDataGcPrefix = "sun.gc"
|
|
hotspotHsPerfDataCiPrefix = "sun.ci"
|
|
)
|
|
|
|
type JvmStatsGcCollector struct {
|
|
Invocations int64 `jvm:"invocations"`
|
|
LastEntryTime int64 `jvm:"lastEntryTime"`
|
|
LastExitTime int64 `jvm:"lastExitTime"`
|
|
Name string `jvm:"name"`
|
|
Time int64 `jvm:"time"`
|
|
}
|
|
|
|
func (c *JvmStatsGcCollector) Parse(p map[string]interface{}) error {
|
|
return parse(p, c)
|
|
}
|
|
|
|
type JvmStatsGcCompressedClassSpace struct {
|
|
Capacity int64 `jvm:"capacity"`
|
|
MaxCapacity int64 `jvm:"maxCapacity"`
|
|
MinCapacity int64 `jvm:"minCapacity"`
|
|
Used int64 `jvm:"used"`
|
|
}
|
|
|
|
func (s *JvmStatsGcCompressedClassSpace) Parse(m map[string]interface{}) error {
|
|
return parse(m, s)
|
|
}
|
|
|
|
type JvmStatsGcGenerationSpace struct {
|
|
Capacity int64 `jvm:"capacity"`
|
|
InitCapacity int64 `jvm:"initCapacity"`
|
|
MaxCapacity int64 `jvm:"maxCapacity"`
|
|
Name string `jvm:"name"`
|
|
Used int64 `jvm:"used"`
|
|
}
|
|
|
|
func (s *JvmStatsGcGenerationSpace) Parse(m map[string]interface{}) error {
|
|
return parse(m, s)
|
|
}
|
|
|
|
type JvmStatsGcGeneration struct {
|
|
AgeTableSize int32 `jvm:"ageTableSize"`
|
|
AgeTableBytes []int64 `jvm:"ageTableBytes,omitempty"`
|
|
Capacity int64 `jvm:"capacity"`
|
|
MaxCapacity int64 `jvm:"maxCapacity"`
|
|
MinCapacity int64 `jvm:"minCapacity"`
|
|
Name string `jvm:"name"`
|
|
Spaces []JvmStatsGcGenerationSpace `jvm:"spaces"`
|
|
}
|
|
|
|
func (g *JvmStatsGcGeneration) Parse(p map[string]interface{}) error {
|
|
if _, ok := p["agetable.size"]; ok {
|
|
g.AgeTableSize = int32(p["agetable.size"].(int64))
|
|
g.AgeTableBytes = make([]int64, g.AgeTableSize)
|
|
for i := int32(0); i < g.AgeTableSize; i++ {
|
|
g.AgeTableBytes[int(i)] = p[fmt.Sprintf("agetable.bytes.%02d", i)].(int64)
|
|
}
|
|
}
|
|
g.Capacity = p["capacity"].(int64)
|
|
g.MinCapacity = p["minCapacity"].(int64)
|
|
g.MaxCapacity = p["maxCapacity"].(int64)
|
|
g.Name = p["name"].(string)
|
|
|
|
spaceSize := int(p["spaces"].(int64))
|
|
g.Spaces = make([]JvmStatsGcGenerationSpace, spaceSize)
|
|
for i := 0; i < spaceSize; i++ {
|
|
err := g.Spaces[i].Parse(filter(p, fmt.Sprintf("space.%d", i)))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
type JvmStatsGcMetaSpace struct {
|
|
Capacity int64 `jvm:"capacity"`
|
|
MaxCapacity int64 `jvm:"maxCapacity"`
|
|
MinCapacity int64 `jvm:"minCapacity"`
|
|
Used int64 `jvm:"used"`
|
|
}
|
|
|
|
func (s *JvmStatsGcMetaSpace) Parse(m map[string]interface{}) error {
|
|
return parse(m, s)
|
|
}
|
|
|
|
type JvmStatsGcPolicy struct {
|
|
Collectors int `jvm:"collectors"`
|
|
DesiredSurvivorSize int64 `jvm:"desiredSurvivorSize"`
|
|
Generations int `jvm:"generations"`
|
|
MaxTenuringThreshold int `jvm:"maxTenuringThreshold"`
|
|
Name string `jvm:"name"`
|
|
TenuringThreshold int `jvm:"tenuringThreshold"`
|
|
}
|
|
|
|
func (p *JvmStatsGcPolicy) Parse(perf map[string]interface{}) error {
|
|
return parse(perf, p)
|
|
}
|
|
|
|
type JvmStatsGcTlab struct {
|
|
Alloc int `jvm:"alloc"`
|
|
AllocThreads int `jvm:"allocThreads"`
|
|
FastWaste int `jvm:"fastWaste"`
|
|
Fills int `jvm:"fills"`
|
|
GcWaste int `jvm:"gcWaste"`
|
|
MaxFastWaste int `jvm:"maxFastWaste"`
|
|
MaxFills int `jvm:"maxFills"`
|
|
MaxGcWaste int `jvm:"maxGcWaste"`
|
|
MaxSlowAlloc int `jvm:"maxSlowAllow"`
|
|
SlowAlloc int `jvm:"slowAlloc"`
|
|
SlowWaste int `jvm:"slowWaste"`
|
|
}
|
|
|
|
func (s *JvmStatsGcTlab) Parse(m map[string]interface{}) error {
|
|
return parse(m, s)
|
|
}
|
|
|
|
type JvmStatsGc struct {
|
|
Cause string `jvm:"cause"`
|
|
LastCause string `jvm:"lastCause"`
|
|
Generations []JvmStatsGcGeneration `jvm:"generations"`
|
|
Collectors []JvmStatsGcCollector `jvm:"collectors"`
|
|
CompressedClassSpace JvmStatsGcCompressedClassSpace `jvm:"compressedclassspace"`
|
|
MetaSpace JvmStatsGcMetaSpace `jvm:"metaspace"`
|
|
Tlab JvmStatsGcTlab `jvm:"tlab"`
|
|
Policy JvmStatsGcPolicy `jvm:"policy"`
|
|
}
|
|
|
|
func filter(perf map[string]interface{}, prefix string) map[string]interface{} {
|
|
r := make(map[string]interface{})
|
|
for k, v := range perf {
|
|
if strings.HasPrefix(k, prefix) {
|
|
r[k[len(prefix)+1:]] = v
|
|
}
|
|
}
|
|
return r
|
|
}
|
|
|
|
func (gc *JvmStatsGc) Parse(perf map[string]interface{}) error {
|
|
gc.Cause = perf["cause"].(string)
|
|
gc.LastCause = perf["lastCause"].(string)
|
|
|
|
if err := gc.Policy.Parse(filter(perf, "policy")); err != nil {
|
|
return err
|
|
}
|
|
|
|
if err := gc.CompressedClassSpace.Parse(filter(perf, "compressedclassspace")); err != nil {
|
|
return err
|
|
}
|
|
|
|
if err := gc.MetaSpace.Parse(filter(perf, "metaspace")); err != nil {
|
|
return err
|
|
}
|
|
|
|
if err := gc.Tlab.Parse(filter(perf, "tlab")); err != nil {
|
|
return err
|
|
}
|
|
|
|
gc.Generations = make([]JvmStatsGcGeneration, 0)
|
|
for i := 0; ; i++ {
|
|
p := filter(perf, fmt.Sprintf("generation.%d", i))
|
|
if len(p) == 0 {
|
|
break
|
|
}
|
|
g := JvmStatsGcGeneration{}
|
|
if err := g.Parse(p); err != nil {
|
|
return err
|
|
}
|
|
gc.Generations = append(gc.Generations, g)
|
|
}
|
|
|
|
gc.Collectors = make([]JvmStatsGcCollector, 0)
|
|
for i := 0; ; i++ {
|
|
p := filter(perf, fmt.Sprintf("collector.%d", i))
|
|
if len(p) == 0 {
|
|
break
|
|
}
|
|
c := JvmStatsGcCollector{}
|
|
if err := c.Parse(p); err != nil {
|
|
return err
|
|
}
|
|
gc.Collectors = append(gc.Collectors, c)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
type JvmStatsJavaThreads struct {
|
|
Daemon int `jvm:"daemon"`
|
|
Live int `jvm:"live"`
|
|
LivePeak int `jvm:"livePeak"`
|
|
Started int `jvm:"started"`
|
|
}
|
|
|
|
func (t *JvmStatsJavaThreads) Parse(m map[string]interface{}) error {
|
|
t.Daemon = int(m["daemon"].(int64))
|
|
t.Live = int(m["live"].(int64))
|
|
t.LivePeak = int(m["livePeak"].(int64))
|
|
t.Started = int(m["started"].(int64))
|
|
|
|
return nil
|
|
}
|
|
|
|
type JvmStatsCiCompilerThread struct {
|
|
Compiles int `jvm:"compiles"`
|
|
Method string `jvm:"method"`
|
|
Time int64 `jvm:"time"`
|
|
Type int `jvm:"type"`
|
|
}
|
|
|
|
func (t *JvmStatsCiCompilerThread) Parse(m map[string]interface{}) error {
|
|
t.Compiles = int(m["compiles"].(int64))
|
|
t.Method = m["method"].(string)
|
|
t.Time = m["time"].(int64)
|
|
t.Type = int(m["type"].(int64))
|
|
return nil
|
|
}
|
|
|
|
type JvmStatsCi struct {
|
|
TotalTime int64 `jvm:"totalTime"`
|
|
LastFailedMethod string `jvm:"lastFailedMethod"`
|
|
LastFailedType int `jvm:"lastFailedType"`
|
|
LastInvalidatedMethod string `jvm:"lastInvalidatedMethod"`
|
|
LastInvalidatedType int `jvm:"lastInvalidatedType"`
|
|
LastMethod string `jvm:"lastMethod"`
|
|
LastSize int `jvm:"lastSize"`
|
|
LastType int `jvm:"lastType"`
|
|
NmethodCodeSize int64 `jvm:"nmethodCodeSize"`
|
|
NmethodSize int64 `jvm:"nmethodSize"`
|
|
OsrBytes int64 `jvm:"osrBytes"`
|
|
OsrCompiles int `jvm:"osrCompiles"`
|
|
OsrTime int64 `jvm:"osrTime"`
|
|
StandardBytes int64 `jvm:"standardBytes"`
|
|
StandardCompiles int `jvm:"standardCompiles"`
|
|
StandardTime int64 `jvm:"standardTime"`
|
|
Threads int `jvm:"threads"`
|
|
TotalBailouts int `jvm:"totalBailouts"`
|
|
TotalCompiles int `jvm:"totalCompiles"`
|
|
TotalInvalidates int `jvm:"totalInvalidates"`
|
|
CompilerThreads []JvmStatsCiCompilerThread
|
|
}
|
|
|
|
func (c *JvmStatsCi) Parse(m map[string]interface{}) error {
|
|
if err := parse(m, c); err != nil {
|
|
return err
|
|
}
|
|
|
|
// JDK 11 doesn't report the details of compiler threads.
|
|
if len(filter(m, "compilerThread.0")) > 0 {
|
|
c.CompilerThreads = make([]JvmStatsCiCompilerThread, c.Threads)
|
|
for i := 0; i < c.Threads; i++ {
|
|
err := c.CompilerThreads[i].Parse(filter(m, fmt.Sprintf("compilerThread.%d", i)))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
type JvmStatsCls struct {
|
|
LoadedClasses int `jvm:"loadedClasses"`
|
|
SharedLoadedClasses int `jvm:"sharedLoadedClasses"`
|
|
SharedUnloadedClasses int `jvm:"sharedUnLoadedClasses"`
|
|
UnloadedClasses int `jvm:"unloadedClasses"`
|
|
|
|
AppClassBytes int64 `jvm:"appClassBytes"`
|
|
AppClassLoadCount int `jvm:"appClassLoadCount"`
|
|
AppClassLoadTime int64 `jvm:"appClassLoadTime"`
|
|
AppClassLoadTimeSelf int64 `jvm:"appClassLoadTime.self"`
|
|
ClassInitTime int64 `jvm:"classInitTime"`
|
|
ClassInitTimeSelf int64 `jvm:"classInitTime.self"`
|
|
ClassLinkedTime int64 `jvm:"classLinkedTime"`
|
|
ClassLinkedTimeSelf int64 `jvm:"classLinkedTime.self"`
|
|
ClassVerifyTime int64 `jvm:"classVerifyTime"`
|
|
ClassVerifyTimeSelf int64 `jvm:"classVerifyTime.self"`
|
|
DefineAppClassTime int64 `jvm:"defineAppClassTime"`
|
|
DefineAppClassTimeSelf int64 `jvm:"defineAppClassTime.self"`
|
|
DefineAppClasses int `jvm:"defineAppClasses"`
|
|
InitializedClasses int `jvm:"initializedClasses"`
|
|
IsUnsyncloadClassSet int `jvm:"isUnsyncloadClassSet"`
|
|
JniDefineClassNoLockCalls int `jvm:"jniDefineClassNoLockCalls"`
|
|
JvmDefineClassNoLockCalls int `jvm:"jvmDefineClassNoLockCalls"`
|
|
JvmFindLoadedClassNoLockCalls int `jvm:"jvmFindLoadedClassNoLockCalls"`
|
|
LinkedClasses int `jvm:"linkedClasses"`
|
|
LoadInstanceClassFailRate int `jvm:"loadInstanceClassFailRate"`
|
|
LoadedBytes int64 `jvm:"loadedBytes"`
|
|
LookupSysClassTime int64 `jvm:"lookupSysClassTime"`
|
|
MethodBytes int64 `jvm:"methodBytes"`
|
|
NonSystemLoaderLockContentionRate int `jvm:"nonSystemLoaderLockContentionRate"`
|
|
ParseClassTime int64 `jvm:"parseClassTime"`
|
|
ParseClassTimeSelf int64 `jvm:"parseClassTime.self"`
|
|
SharedClassLoadTime int64 `jvm:"sharedClassLoadTime"`
|
|
SharedLoadedBytes int64 `jvm:"sharedLoadedBytes"`
|
|
SharedUnloadedBytes int64 `jvm:"sharedUnloadedBytes"`
|
|
SysClassBytes int64 `jvm:"sysClassBytes"`
|
|
SysClassLoadTime int64 `jvm:"sysClassLoadTime"`
|
|
SystemLoaderLockContentionRate int `jvm:"systemLoaderLockContentionRate"`
|
|
Time int64 `jvm:"time"`
|
|
UnloadedBytes int64 `jvm:"unloadedBytes"`
|
|
UnsafeDefineClassCalls int `jvm:"unsafeDefineClassCalls"`
|
|
VerifiedClasses int `jvm:"verifiedClasses"`
|
|
}
|
|
|
|
func (c *JvmStatsCls) ParseJava(m map[string]interface{}) error {
|
|
return parse(m, c)
|
|
}
|
|
|
|
func (c *JvmStatsCls) ParseSun(m map[string]interface{}) error {
|
|
return parse(m, c)
|
|
}
|
|
|
|
type JvmStatsClassLoader struct {
|
|
FindClassTime int64 `jvm:"findClassTime"`
|
|
FindClasses int `jvm:"findClasses"`
|
|
ParentDelegationTime int64 `jvm:"parentDelegationTime"`
|
|
UrlReadClassBytesTime int64 `jvm:"readClassBytesTime"`
|
|
}
|
|
|
|
func (l *JvmStatsClassLoader) Parse(m map[string]interface{}) error {
|
|
return parse(m, l)
|
|
}
|
|
|
|
type JvmStatsRt struct {
|
|
SyncContentedLockAttempts int `jvm:"_sync_ContendedLockAttempts"`
|
|
SyncDeflations int `jvm:"_sync_Deflations"`
|
|
SyncEmptyNotifications int `jvm:"_sync_emptyNotifications"`
|
|
SyncFailedSpins int `jvm:"_sync_FailedSpins"`
|
|
SyncFutileWakeups int `jvm:"_sync_FutileWakeups"`
|
|
SyncInflations int `jvm:"_sync_Inflations"`
|
|
SyncMonExtant int `jvm:"_sync_MonExtant"`
|
|
SyncMonInCirculation int `jvm:"_sync_MonInCirculation"`
|
|
SyncMonScavenged int `jvm:"_sync_MonScavenged"`
|
|
SyncNotifications int `jvm:"_sync_Notifications"`
|
|
SyncParks int `jvm:"_sync_Parks"`
|
|
SyncPrivateA int `jvm:"_sync_PrivateA"`
|
|
SyncPrivateB int `jvm:"_sync_PrivateB"`
|
|
SyncSlowEnter int `jvm:"_sync_SlowEnter"`
|
|
SyncSlowExit int `jvm:"_sync_SlowExit"`
|
|
SyncSlowNotify int `jvm:"_sync_SlowNotify"`
|
|
SyncSlowNotifyAll int `jvm:"_sync_SlowNotifyAll"`
|
|
SyncSuccessfulSpins int `jvm:"_sync_SuccessfulSpins"`
|
|
ApplicationTime int64 `jvm:"applicationTime"`
|
|
CreateVmBeginTime int64 `jvm:"CreateVmBeginTime"`
|
|
CreateVmEndTime int64 `jvm:"CreateVmEndTime"`
|
|
InternalVersion string `jvm:"internalVersion"`
|
|
InterruptedBeforeIO int `jvm:"interruptedBeforeIO"`
|
|
InterruptedDuringIO int `jvm:"interruptedDuringIO"`
|
|
JavaCommand string `jvm:"javaCommand"`
|
|
JvmCapabilities string `jvm:"jvmCapabilities"`
|
|
JvmVersion int `jvm:"jvmVersion"`
|
|
SafePointSyncTime int64 `jvm:"safepointSyncTime"`
|
|
SafePointTime int64 `jvm:"safepointTime"`
|
|
SafePoints int64 `jvm:"safepoints"`
|
|
ThreadInterruptSignaled int `jvm:"threadInterruptSignaled"`
|
|
VmInitDoneTime int64 `jvm:"vmInitDoneTime"`
|
|
}
|
|
|
|
func (r *JvmStatsRt) Parse(m map[string]interface{}) error {
|
|
return parse(m, r)
|
|
}
|
|
|
|
type JvmStatsOsHrt struct {
|
|
Frequency int64 `jvm:"frequency"`
|
|
Ticks int64 `jvm:"ticks"`
|
|
}
|
|
|
|
type JvmStatsOs struct {
|
|
Hrt JvmStatsOsHrt `jvm:"hrt"`
|
|
}
|
|
|
|
func (o *JvmStatsOs) Parse(m map[string]interface{}) error {
|
|
return parse(m, o)
|
|
}
|
|
|
|
// JVM stats from hsperfdata, only java.threads and sun.gc, others are left unparsed
|
|
type JvmStats struct {
|
|
JavaThreads JvmStatsJavaThreads `jvm:"java.threads"`
|
|
Gc JvmStatsGc `jvm:"sun.gc"`
|
|
Ci JvmStatsCi `jvm:"sun.ci"`
|
|
Cls JvmStatsCls `jvm:"java.cls,sun.cls"`
|
|
ClsLoader JvmStatsClassLoader `jvm:"sun.classloader"`
|
|
Rt JvmStatsRt `jvm:"sun.rt"`
|
|
Os JvmStatsOs `jvm:"sun.os"`
|
|
}
|
|
|
|
func (s *JvmStats) Parse(perf map[string]interface{}) error {
|
|
var err error
|
|
|
|
err = s.Rt.Parse(filter(perf, "sun.rt"))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
err = s.Os.Parse(filter(perf, "sun.os"))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
err = s.JavaThreads.Parse(filter(perf, hotspotHsPerfDataJavaThreadsPrefix))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
err = s.Gc.Parse(filter(perf, hotspotHsPerfDataGcPrefix))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
s.Ci.TotalTime = perf["java.ci.totalTime"].(int64)
|
|
err = s.Ci.Parse(filter(perf, hotspotHsPerfDataCiPrefix))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
err = s.Cls.ParseJava(filter(perf, "java.cls"))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
err = s.Cls.ParseSun(filter(perf, "sun.cls"))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
err = parse(filter(perf, "sun.urlClassLoader"), &s.ClsLoader)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
err = s.ClsLoader.Parse(filter(perf, "sun.classloader"))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func parse(m map[string]interface{}, x interface{}) error {
|
|
t := reflect.TypeOf(x).Elem()
|
|
v := reflect.ValueOf(x).Elem()
|
|
|
|
for i := 0; i < t.NumField(); i++ {
|
|
f := t.Field(i)
|
|
vf := v.Field(i)
|
|
if tag, ok := f.Tag.Lookup("jvm"); ok {
|
|
switch f.Type.Kind() {
|
|
case reflect.Struct:
|
|
if err := parse(filter(m, tag), vf.Addr().Interface()); err != nil {
|
|
return err
|
|
}
|
|
case reflect.String:
|
|
if s, ok := m[tag]; ok {
|
|
v.Field(i).SetString(strings.Trim(s.(string), "\x00"))
|
|
}
|
|
case reflect.Int:
|
|
fallthrough
|
|
case reflect.Int8:
|
|
fallthrough
|
|
case reflect.Int16:
|
|
fallthrough
|
|
case reflect.Int32:
|
|
fallthrough
|
|
case reflect.Int64:
|
|
if s, ok := m[tag]; ok {
|
|
v.Field(i).SetInt(s.(int64))
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|