github.com/pinpoint-apm/pinpoint-go-agent@v1.4.1-0.20240110120318-a50c2eb18c8c/stats.go (about)

     1  package pinpoint
     2  
     3  import (
     4  	"os"
     5  	"runtime"
     6  	"sync"
     7  	"sync/atomic"
     8  	"time"
     9  
    10  	"github.com/shirou/gopsutil/v3/cpu"
    11  	"github.com/shirou/gopsutil/v3/process"
    12  )
    13  
    14  type inspectorStats struct {
    15  	sampleTime   time.Time
    16  	interval     int64
    17  	cpuProcLoad  float64
    18  	cpuSysLoad   float64
    19  	heapUsed     int64
    20  	heapMax      int64
    21  	nonHeapUsed  int64
    22  	nonHeapMax   int64
    23  	gcNum        int64
    24  	gcTime       int64
    25  	numOpenFD    int64
    26  	numThreads   int64
    27  	responseAvg  int64
    28  	responseMax  int64
    29  	sampleNew    int64
    30  	sampleCont   int64
    31  	unSampleNew  int64
    32  	unSampleCont int64
    33  	skipNew      int64
    34  	skipCont     int64
    35  	activeSpan   []int32
    36  }
    37  
    38  var (
    39  	proc            *process.Process
    40  	lastMemStat     runtime.MemStats
    41  	lastCollectTime time.Time
    42  
    43  	accResponseTime int64
    44  	maxResponseTime int64
    45  	requestCount    int64
    46  
    47  	sampleNew    int64
    48  	unSampleNew  int64
    49  	sampleCont   int64
    50  	unSampleCont int64
    51  	skipNew      int64
    52  	skipCont     int64
    53  
    54  	activeSpan sync.Map
    55  )
    56  
    57  func initStats() {
    58  	var err error
    59  	proc, err = process.NewProcess(int32(os.Getpid()))
    60  	if err != nil {
    61  		proc = nil
    62  	} else {
    63  		proc.Percent(0)
    64  	}
    65  
    66  	cpu.Percent(0, false)
    67  	runtime.ReadMemStats(&lastMemStat)
    68  	lastCollectTime = time.Now()
    69  	activeSpan = sync.Map{}
    70  }
    71  
    72  func getNumFD() int32 {
    73  	if proc != nil {
    74  		n, _ := proc.NumFDs()
    75  		return n
    76  	}
    77  	return 0
    78  }
    79  
    80  func getNumThreads() int32 {
    81  	if proc != nil {
    82  		n, _ := proc.NumThreads()
    83  		return n
    84  	}
    85  	return 0
    86  }
    87  
    88  func getCpuLoad() (float64, float64) {
    89  	var procCpu float64
    90  	if proc != nil {
    91  		procCpu, _ = proc.Percent(0)
    92  	} else {
    93  		procCpu = 0
    94  	}
    95  	sysCpu, _ := cpu.Percent(0, false)
    96  
    97  	return procCpu / 100, sysCpu[0] / 100
    98  }
    99  
   100  func getStats() *inspectorStats {
   101  	now := time.Now()
   102  	procCpu, sysCpu := getCpuLoad()
   103  
   104  	var memStat runtime.MemStats
   105  	runtime.ReadMemStats(&memStat)
   106  	elapsed := now.Sub(lastCollectTime).Seconds()
   107  
   108  	stats := inspectorStats{
   109  		sampleTime:   now,
   110  		interval:     int64(elapsed) * 1000,
   111  		cpuProcLoad:  procCpu,
   112  		cpuSysLoad:   sysCpu,
   113  		heapUsed:     int64(memStat.HeapInuse),
   114  		heapMax:      int64(memStat.HeapSys),
   115  		nonHeapUsed:  int64(memStat.StackInuse),
   116  		nonHeapMax:   int64(memStat.StackSys),
   117  		gcNum:        int64(memStat.NumGC - lastMemStat.NumGC),
   118  		gcTime:       int64(memStat.PauseTotalNs-lastMemStat.PauseTotalNs) / int64(time.Millisecond),
   119  		numOpenFD:    int64(getNumFD()),
   120  		numThreads:   int64(getNumThreads()),
   121  		responseAvg:  calcResponseAvg(),
   122  		responseMax:  maxResponseTime,
   123  		sampleNew:    sampleNew,
   124  		sampleCont:   sampleCont,
   125  		unSampleNew:  unSampleNew,
   126  		unSampleCont: unSampleCont,
   127  		skipNew:      skipNew,
   128  		skipCont:     skipCont,
   129  		activeSpan:   activeSpanCount(now),
   130  	}
   131  
   132  	lastMemStat = memStat
   133  	lastCollectTime = now
   134  	resetResponseTime()
   135  
   136  	return &stats
   137  }
   138  
   139  func calcResponseAvg() int64 {
   140  	if requestCount > 0 {
   141  		return accResponseTime / requestCount
   142  	}
   143  
   144  	return 0
   145  }
   146  
   147  func activeSpanCount(now time.Time) []int32 {
   148  	count := []int32{0, 0, 0, 0}
   149  	activeSpan.Range(func(k, v interface{}) bool {
   150  		s := v.(time.Time)
   151  		d := now.Sub(s).Seconds()
   152  
   153  		if d < 1 {
   154  			count[0]++
   155  		} else if d < 3 {
   156  			count[1]++
   157  		} else if d < 5 {
   158  			count[2]++
   159  		} else {
   160  			count[3]++
   161  		}
   162  		return true
   163  	})
   164  
   165  	return count
   166  }
   167  
   168  func (agent *agent) collectAgentStatWorker() {
   169  	Log("stats").Infof("start collect agent stat goroutine")
   170  	defer agent.wg.Done()
   171  
   172  	initStats()
   173  	resetResponseTime()
   174  
   175  	interval := time.Duration(agent.config.Int(CfgStatCollectInterval)) * time.Millisecond
   176  	time.Sleep(interval)
   177  	cfgBatchCount := agent.config.Int(CfgStatBatchCount)
   178  	collected := make([]*inspectorStats, cfgBatchCount)
   179  	batch := 0
   180  
   181  	for agent.enable {
   182  		collected[batch] = getStats()
   183  		batch++
   184  
   185  		if batch == cfgBatchCount {
   186  			agent.enqueueStat(makePAgentStatBatch(collected))
   187  			batch = 0
   188  		}
   189  		time.Sleep(interval)
   190  	}
   191  	Log("stats").Infof("end collect agent stat goroutine")
   192  }
   193  
   194  func collectResponseTime(resTime int64) {
   195  	atomic.AddInt64(&accResponseTime, resTime)
   196  	atomic.AddInt64(&requestCount, 1)
   197  
   198  	if atomic.LoadInt64(&maxResponseTime) < resTime {
   199  		atomic.StoreInt64(&maxResponseTime, resTime)
   200  	}
   201  }
   202  
   203  func resetResponseTime() {
   204  	atomic.StoreInt64(&accResponseTime, 0)
   205  	atomic.StoreInt64(&requestCount, 0)
   206  	atomic.StoreInt64(&maxResponseTime, 0)
   207  	atomic.StoreInt64(&sampleNew, 0)
   208  	atomic.StoreInt64(&unSampleNew, 0)
   209  	atomic.StoreInt64(&sampleCont, 0)
   210  	atomic.StoreInt64(&unSampleCont, 0)
   211  	atomic.StoreInt64(&skipNew, 0)
   212  	atomic.StoreInt64(&skipCont, 0)
   213  }
   214  
   215  func addSampledActiveSpan(span *span) {
   216  	activeSpan.Store(span.spanId, span.startTime)
   217  	addRealTimeSampledActiveSpan(span)
   218  }
   219  
   220  func dropSampledActiveSpan(span *span) {
   221  	activeSpan.Delete(span.spanId)
   222  	dropRealTimeSampledActiveSpan(span)
   223  }
   224  
   225  func addUnSampledActiveSpan(span *noopSpan) {
   226  	activeSpan.Store(span.spanId, span.startTime)
   227  	addRealTimeUnSampledActiveSpan(span)
   228  }
   229  
   230  func dropUnSampledActiveSpan(span *noopSpan) {
   231  	activeSpan.Delete(span.spanId)
   232  	dropRealTimeUnSampledActiveSpan(span)
   233  }
   234  
   235  func incrSampleNew() {
   236  	atomic.AddInt64(&sampleNew, 1)
   237  }
   238  func incrUnSampleNew() {
   239  	atomic.AddInt64(&unSampleNew, 1)
   240  }
   241  func incrSampleCont() {
   242  	atomic.AddInt64(&sampleCont, 1)
   243  }
   244  func incrUnSampleCont() {
   245  	atomic.AddInt64(&unSampleCont, 1)
   246  }
   247  func incrSkipNew() {
   248  	atomic.AddInt64(&skipNew, 1)
   249  }
   250  func incrSkipCont() {
   251  	atomic.AddInt64(&skipCont, 1)
   252  }