package containers import ( "bytes" "fmt" "path/filepath" "strconv" "time" "github.com/coroot/coroot-node-agent/common" "github.com/coroot/coroot-node-agent/proc" "github.com/prometheus/client_golang/prometheus" klog "github.com/sirupsen/logrus" "github.com/xin053/hsperfdata" ) func isJvm(cmdline []byte) bool { idx := bytes.Index(cmdline, []byte{0}) if idx < 0 { return false } return bytes.HasSuffix(cmdline[:idx], []byte("java")) } func jvmMetrics(pid uint32) (string, []prometheus.Metric) { p := proc.Path(pid, "root/tmp/hsperfdata_*/"+strconv.Itoa(int(proc.GetNsPid(pid)))) files, _ := filepath.Glob(p) if len(files) != 1 { return "", nil } pd, err := readPerfData(files[0]) if err != nil { if !common.IsNotExist(err) { klog.Warningln(err) } return "", nil } jvm := pd.getString("sun.rt.javaCommand") var res []prometheus.Metric res = append(res, gauge(metrics.JvmInfo, 1, jvm, pd.getString("java.property.java.version"))) func() { size := float64(0) used := float64(0) for _, gen := range []int{0, 1} { spaces := pd.getInt64("sun.gc.generation.%d.spaces", gen) for s := 0; s < int(spaces); s++ { size += float64(pd.getInt64("sun.gc.generation.%d.space.%d.capacity", gen, s)) used += float64(pd.getInt64("sun.gc.generation.%d.space.%d.used", gen, s)) } } res = append(res, gauge(metrics.JvmHeapSize, size, jvm)) res = append(res, gauge(metrics.JvmHeapUsed, used, jvm)) }() gc := func(prefix string) { name := pd.getString(prefix + "name") if name == "" { return } res = append(res, counter(metrics.JvmGCTime, time.Duration(pd.getInt64(prefix+"time")).Seconds(), jvm, name)) } gc("sun.gc.collector.0.") gc("sun.gc.collector.1.") gc("sun.gc.collector.2.") res = append(res, counter(metrics.JvmSafepointTime, time.Duration(pd.getInt64("sun.rt.safepointTime")).Seconds(), jvm)) res = append(res, counter(metrics.JvmSafepointSyncTime, time.Duration(pd.getInt64("sun.rt.safepointSyncTime")).Seconds(), jvm)) return jvm, res } type perfData struct { data map[string]interface{} } func readPerfData(p string) (*perfData, error) { data, err := hsperfdata.ReadPerfData(p, true) return &perfData{data: data}, err } func (pd *perfData) getString(key string) string { v, _ := pd.data[key].(string) return v } func (pd *perfData) getInt64(key string, a ...any) int64 { if len(a) > 0 { key = fmt.Sprintf(key, a...) } v, _ := pd.data[key].(int64) return v }