polardbxoperator/pkg/exporter/extensions/jvm/hotspot_jvmstat.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
}