// getProcFDUsage returns file descriptor usage information for the process // identified by the given PID. If the feature is not implemented then nil // is returned with no error. If there is a permission error while reading the // data then nil is returned with no error (/proc/[pid]/fd requires root // permissions). Any other errors that occur are returned. func getProcFDUsage(pid int) (*sigar.ProcFDUsage, error) { fd := sigar.ProcFDUsage{} if err := fd.Get(pid); err != nil { switch { case sigar.IsNotImplemented(err): return nil, nil case os.IsPermission(err): return nil, nil default: return nil, err } } return &fd, nil }
// getProcFDUsage returns file descriptor usage information for the process // identified by the given PID. If the feature is not implemented then nil // is returned with no error. If there is a permission error while reading the // data then nil is returned with no error (/proc/[pid]/fd requires root // permissions). Any other errors that occur are returned. func getProcFDUsage(pid int) (*sigar.ProcFDUsage, error) { // It's not possible to collect FD usage from other processes on FreeBSD // due to linprocfs not exposing the information. if runtime.GOOS == "freebsd" && pid != os.Getpid() { return nil, nil } fd := sigar.ProcFDUsage{} if err := fd.Get(pid); err != nil { switch { case sigar.IsNotImplemented(err): return nil, nil case os.IsPermission(err): return nil, nil default: return nil, err } } return &fd, nil }
// SampleEnvironment queries the runtime system for various interesting metrics, // storing the resulting values in the set of metric gauges maintained by // RuntimeStatSampler. This makes runtime statistics more convenient for // consumption by the time series and status systems. // // This method should be called periodically by a higher level system in order // to keep runtime statistics current. func (rsr *RuntimeStatSampler) SampleEnvironment() { // Record memory and call stats from the runtime package. // TODO(mrtracy): memory statistics will not include usage from RocksDB. // Determine an appropriate way to compute total memory usage. numCgoCall := runtime.NumCgoCall() numGoroutine := runtime.NumGoroutine() // It might be useful to call ReadMemStats() more often, but it stops the // world while collecting stats so shouldn't be called too often. // NOTE: the MemStats fields do not get decremented when memory is released, // to get accurate numbers, be sure to subtract. eg: ms.Sys - ms.HeapReleased for // current memory reserved. ms := runtime.MemStats{} runtime.ReadMemStats(&ms) // Retrieve Mem and CPU statistics. pid := os.Getpid() mem := gosigar.ProcMem{} if err := mem.Get(pid); err != nil { log.Errorf(context.TODO(), "unable to get mem usage: %v", err) } cpu := gosigar.ProcTime{} if err := cpu.Get(pid); err != nil { log.Errorf(context.TODO(), "unable to get cpu usage: %v", err) } fds := gosigar.ProcFDUsage{} if err := fds.Get(pid); err != nil { if _, ok := err.(gosigar.ErrNotImplemented); ok { if !rsr.fdUsageNotImplemented { rsr.fdUsageNotImplemented = true log.Errorf(context.TODO(), "unable to get file descriptor usage (will not try again): %s", err) } } else { log.Errorf(context.TODO(), "unable to get file descriptor usage: %s", err) } } // Time statistics can be compared to the total elapsed time to create a // useful percentage of total CPU usage, which would be somewhat less accurate // if calculated later using downsampled time series data. now := rsr.clock.PhysicalNow() dur := float64(now - rsr.lastNow) // cpu.{User,Sys} are in milliseconds, convert to nanoseconds. newUtime := int64(cpu.User) * 1e6 newStime := int64(cpu.Sys) * 1e6 uPerc := float64(newUtime-rsr.lastUtime) / dur sPerc := float64(newStime-rsr.lastStime) / dur pausePerc := float64(ms.PauseTotalNs-rsr.lastPauseTime) / dur rsr.lastNow = now rsr.lastUtime = newUtime rsr.lastStime = newStime rsr.lastPauseTime = ms.PauseTotalNs var cgoAllocated, cgoTotal uint64 if getCgoMemStats != nil { var err error cgoAllocated, cgoTotal, err = getCgoMemStats() if err != nil { log.Warningf(context.TODO(), "problem fetching CGO memory stats: %s, CGO stats will be empty.", err) } } goAllocated := ms.Alloc goTotal := ms.Sys - ms.HeapReleased // Log summary of statistics to console. cgoRate := float64((numCgoCall-rsr.lastCgoCall)*int64(time.Second)) / dur log.Infof(context.TODO(), "runtime stats: %s RSS, %d goroutines, %s/%s/%s GO alloc/idle/total, %s/%s CGO alloc/total, %.2fcgo/sec, %.2f/%.2f %%(u/s)time, %.2f %%gc (%dx)", humanize.IBytes(mem.Resident), numGoroutine, humanize.IBytes(goAllocated), humanize.IBytes(ms.HeapIdle-ms.HeapReleased), humanize.IBytes(goTotal), humanize.IBytes(cgoAllocated), humanize.IBytes(cgoTotal), cgoRate, uPerc, sPerc, pausePerc, ms.NumGC-rsr.lastNumGC) if log.V(2) { log.Infof(context.TODO(), "memstats: %+v", ms) } rsr.lastCgoCall = numCgoCall rsr.lastNumGC = ms.NumGC rsr.CgoCalls.Update(numCgoCall) rsr.Goroutines.Update(int64(numGoroutine)) rsr.GoAllocBytes.Update(int64(goAllocated)) rsr.GoTotalBytes.Update(int64(goTotal)) rsr.CgoAllocBytes.Update(int64(cgoAllocated)) rsr.CgoTotalBytes.Update(int64(cgoTotal)) rsr.GcCount.Update(int64(ms.NumGC)) rsr.GcPauseNS.Update(int64(ms.PauseTotalNs)) rsr.GcPausePercent.Update(pausePerc) rsr.CPUUserNS.Update(newUtime) rsr.CPUUserPercent.Update(uPerc) rsr.CPUSysNS.Update(newStime) rsr.CPUSysPercent.Update(sPerc) rsr.FDOpen.Update(int64(fds.Open)) rsr.FDSoftLimit.Update(int64(fds.SoftLimit)) rsr.Rss.Update(int64(mem.Resident)) rsr.Uptime.Update((now - rsr.startTimeNanos) / 1e9) }