Files
httprunner/hrp/pkg/gidevice/perfd.go
2022-11-29 22:11:20 +08:00

894 lines
23 KiB
Go

package gidevice
import (
"context"
"encoding/binary"
"encoding/json"
"fmt"
"log"
"net"
"strconv"
"time"
"github.com/httprunner/httprunner/v4/hrp/pkg/gidevice/pkg/libimobiledevice"
)
type PerfOptions struct {
// system
SysCPU bool `json:"sys_cpu,omitempty" yaml:"sys_cpu,omitempty"`
SysMem bool `json:"sys_mem,omitempty" yaml:"sys_mem,omitempty"`
SysDisk bool `json:"sys_disk,omitempty" yaml:"sys_disk,omitempty"`
SysNetwork bool `json:"sys_network,omitempty" yaml:"sys_network,omitempty"`
gpu bool
FPS bool `json:"fps,omitempty" yaml:"fps,omitempty"`
Network bool `json:"network,omitempty" yaml:"network,omitempty"`
// process
BundleID string `json:"bundle_id,omitempty" yaml:"bundle_id,omitempty"`
Pid int `json:"pid,omitempty" yaml:"pid,omitempty"`
// config
OutputInterval int `json:"output_interval,omitempty" yaml:"output_interval,omitempty"` // ms
SystemAttributes []string `json:"system_attributes,omitempty" yaml:"system_attributes,omitempty"`
ProcessAttributes []string `json:"process_attributes,omitempty" yaml:"process_attributes,omitempty"`
}
func defaulPerfOption() *PerfOptions {
return &PerfOptions{
SysCPU: false,
SysMem: false,
SysDisk: false,
SysNetwork: false,
gpu: false,
FPS: false,
Network: false,
OutputInterval: 1000, // default 1000ms
SystemAttributes: []string{
// disk
"diskBytesRead",
"diskBytesWritten",
"diskReadOps",
"diskWriteOps",
// memory
"vmCompressorPageCount",
"vmExtPageCount",
"vmFreeCount",
"vmIntPageCount",
"vmPurgeableCount",
"vmWireCount",
"vmUsedCount",
"__vmSwapUsage",
// network
"netBytesIn",
"netBytesOut",
"netPacketsIn",
"netPacketsOut",
},
ProcessAttributes: []string{
"pid",
"cpuUsage",
},
}
}
type PerfOption func(*PerfOptions)
func WithPerfSystemCPU(b bool) PerfOption {
return func(opt *PerfOptions) {
opt.SysCPU = b
}
}
func WithPerfSystemMem(b bool) PerfOption {
return func(opt *PerfOptions) {
opt.SysMem = b
}
}
func WithPerfSystemDisk(b bool) PerfOption {
return func(opt *PerfOptions) {
opt.SysDisk = b
}
}
func WithPerfSystemNetwork(b bool) PerfOption {
return func(opt *PerfOptions) {
opt.SysNetwork = b
}
}
func WithPerfBundleID(bundleID string) PerfOption {
return func(opt *PerfOptions) {
opt.BundleID = bundleID
}
}
func WithPerfPID(pid int) PerfOption {
return func(opt *PerfOptions) {
opt.Pid = pid
}
}
func WithPerfGPU(b bool) PerfOption {
return func(opt *PerfOptions) {
opt.gpu = b
}
}
func WithPerfFPS(b bool) PerfOption {
return func(opt *PerfOptions) {
opt.FPS = b
}
}
func WithPerfNetwork(b bool) PerfOption {
return func(opt *PerfOptions) {
opt.Network = b
}
}
func WithPerfOutputInterval(intervalMilliseconds int) PerfOption {
return func(opt *PerfOptions) {
opt.OutputInterval = intervalMilliseconds
}
}
func WithPerfProcessAttributes(attrs ...string) PerfOption {
return func(opt *PerfOptions) {
opt.ProcessAttributes = attrs
}
}
func WithPerfSystemAttributes(attrs ...string) PerfOption {
return func(opt *PerfOptions) {
opt.SystemAttributes = attrs
}
}
type perfdClient struct {
options *PerfOptions
i Instruments
stop chan struct{} // used to stop perf client
cancel context.CancelFunc // used to cancel all iterators
}
func (d *device) newPerfdSysmontap(options *PerfOptions) (*perfdSysmontap, error) {
instruments, err := d.newInstrumentsService()
if err != nil {
return nil, err
}
return &perfdSysmontap{
perfdClient: perfdClient{
i: instruments,
options: options,
stop: make(chan struct{}),
},
chanSysCPU: make(chan []byte, 10),
chanSysMem: make(chan []byte, 10),
chanSysDisk: make(chan []byte, 10),
chanSysNetwork: make(chan []byte, 10),
chanProcess: make(chan []byte, 10),
}, nil
}
type perfdSysmontap struct {
perfdClient
chanSysCPU chan []byte // system cpu channel
chanSysMem chan []byte // system mem channel
chanSysDisk chan []byte // system disk channel
chanSysNetwork chan []byte // system network channel
chanProcess chan []byte // process channel
}
func (c *perfdSysmontap) Start() (data <-chan []byte, err error) {
// set config
interval := time.Millisecond * time.Duration(c.options.OutputInterval)
log.Printf("set sysmontap sample interval: %dms\n", c.options.OutputInterval)
config := map[string]interface{}{
"bm": 0,
"cpuUsage": true,
"sampleInterval": interval, // time.Duration
"ur": c.options.OutputInterval, // 输出频率
"procAttrs": c.options.ProcessAttributes, // process performance
"sysAttrs": c.options.SystemAttributes, // system performance
}
if _, err = c.i.call(
instrumentsServiceSysmontap,
"setConfig:",
config,
); err != nil {
return nil, err
}
// start
if _, err = c.i.call(
instrumentsServiceSysmontap,
"start",
); err != nil {
return nil, err
}
// register listener
ctx, cancel := context.WithCancel(context.TODO())
c.i.registerCallback("", func(m libimobiledevice.DTXMessageResult) {
select {
case <-ctx.Done():
c.i.call(instrumentsServiceSysmontap, "stop")
return
default:
dataArray, ok := m.Obj.([]interface{})
if !ok || len(dataArray) != 2 {
return
}
if c.options.Pid != 0 {
c.parseProcessData(dataArray)
} else {
c.parseSystemData(dataArray)
}
}
})
c.cancel = cancel
outCh := make(chan []byte, 100)
go func() {
for {
select {
case <-c.stop:
c.cancel()
return
case cpuBytes, ok := <-c.chanSysCPU:
if ok {
outCh <- cpuBytes
}
case memBytes, ok := <-c.chanSysMem:
if ok {
outCh <- memBytes
}
case diskBytes, ok := <-c.chanSysDisk:
if ok {
outCh <- diskBytes
}
case networkBytes, ok := <-c.chanSysNetwork:
if ok {
outCh <- networkBytes
}
case processBytes, ok := <-c.chanProcess:
if ok {
outCh <- processBytes
}
}
}
}()
return outCh, nil
}
func (c *perfdSysmontap) Stop() {
close(c.stop)
}
func (c *perfdSysmontap) parseProcessData(dataArray []interface{}) {
// dataArray example:
// [
// map[
// CPUCount:2
// EnabledCPUs:2
// PerCPUUsage:[
// map[CPU_NiceLoad:0 CPU_SystemLoad:-1 CPU_TotalLoad:3.6363636363636402 CPU_UserLoad:-1]
// map[CPU_NiceLoad:0 CPU_SystemLoad:-1 CPU_TotalLoad:2.7272727272727195 CPU_UserLoad:-1]
// ]
// System:[36408520704 6897049600 3031160 773697 15596 61940 1297 26942 588 17020 127346 1835008 119718056 107009899 174046 103548]
// SystemCPUUsage:map[CPU_NiceLoad:0 CPU_SystemLoad:-1 CPU_TotalLoad:6.36363636363636 CPU_UserLoad:-1]
// StartMachAbsTime:5896602132889
// EndMachAbsTime:5896628486761
// Type:41
// ]
// map[
// Processes:map[
// 0:[1.3582834340402803 0]
// 124:[0.011456702068519481 124]
// 136:[0.05468332721703649 136]
// ]
// StartMachAbsTime:5896602295095
// EndMachAbsTime:5896628780514
// Type:5
// ]
// ]
processData := make(map[string]interface{})
processData["type"] = "process"
processData["timestamp"] = time.Now().Unix()
processData["pid"] = c.options.Pid
defer func() {
processBytes, _ := json.Marshal(processData)
c.chanProcess <- processBytes
}()
systemInfo := dataArray[0].(map[string]interface{})
processInfo := dataArray[1].(map[string]interface{})
if _, ok := systemInfo["System"]; !ok {
systemInfo, processInfo = processInfo, systemInfo
}
var targetProcessValue []interface{}
processList := processInfo["Processes"].(map[string]interface{})
for pid, v := range processList {
if pid != strconv.Itoa(c.options.Pid) {
continue
}
targetProcessValue = v.([]interface{})
}
if targetProcessValue == nil {
processData["msg"] = fmt.Sprintf("process %d not found", c.options.Pid)
return
}
processAttributesMap := make(map[string]interface{})
for idx, value := range c.options.ProcessAttributes {
processAttributesMap[value] = targetProcessValue[idx]
}
processData["proc_perf"] = processAttributesMap
systemAttributesValue := systemInfo["System"].([]interface{})
systemAttributesMap := make(map[string]int64)
for idx, value := range c.options.SystemAttributes {
systemAttributesMap[value] = convert2Int64(systemAttributesValue[idx])
}
processData["sys_perf"] = systemAttributesMap
}
func (c *perfdSysmontap) parseSystemData(dataArray []interface{}) {
timestamp := time.Now().Unix()
var systemInfo map[string]interface{}
data1 := dataArray[0].(map[string]interface{})
data2 := dataArray[1].(map[string]interface{})
if _, ok := data1["SystemCPUUsage"]; ok {
systemInfo = data1
} else {
systemInfo = data2
}
// systemInfo example:
// map[
// CPUCount:2
// EnabledCPUs:2
// PerCPUUsage:[
// map[CPU_NiceLoad:0 CPU_SystemLoad:-1 CPU_TotalLoad:3.9215686274509807 CPU_UserLoad:-1]
// map[CPU_NiceLoad:0 CPU_SystemLoad:-1 CPU_TotalLoad:11.650485436893206 CPU_UserLoad:-1]]
// ]
// System:[704211 35486281728 6303789056 3001119 1001 11033 52668 1740 40022 2114 17310 126903 1835008 160323 107909856 95067 95808179]
// SystemCPUUsage:map[
// CPU_NiceLoad:0
// CPU_SystemLoad:-1
// CPU_TotalLoad:15.572054064344186
// CPU_UserLoad:-1
// ]
// StartMachAbsTime:5339240248449
// EndMachAbsTime:5339264441260
// Type:41
// ]
if c.options.SysCPU {
sysCPUUsage := systemInfo["SystemCPUUsage"].(map[string]interface{})
sysCPUInfo := SystemCPUData{
PerfDataBase: PerfDataBase{
Type: "sys_cpu",
TimeStamp: timestamp,
},
NiceLoad: sysCPUUsage["CPU_NiceLoad"].(float64),
SystemLoad: sysCPUUsage["CPU_SystemLoad"].(float64),
TotalLoad: sysCPUUsage["CPU_TotalLoad"].(float64),
UserLoad: sysCPUUsage["CPU_UserLoad"].(float64),
}
cpuBytes, _ := json.Marshal(sysCPUInfo)
c.chanSysCPU <- cpuBytes
}
systemAttributesValue := systemInfo["System"].([]interface{})
systemAttributesMap := make(map[string]int64)
for idx, value := range c.options.SystemAttributes {
systemAttributesMap[value] = convert2Int64(systemAttributesValue[idx])
}
if c.options.SysMem {
kernelPageSize := int64(1) // why 16384 ?
appMemory := (systemAttributesMap["vmIntPageCount"] - systemAttributesMap["vmPurgeableCount"]) * kernelPageSize
cachedFiles := (systemAttributesMap["vmExtPageCount"] - systemAttributesMap["vmPurgeableCount"]) * kernelPageSize
compressed := systemAttributesMap["vmCompressorPageCount"] * kernelPageSize
usedMemory := (systemAttributesMap["vmUsedCount"] - systemAttributesMap["vmExtPageCount"]) * kernelPageSize
wiredMemory := systemAttributesMap["vmWireCount"] * kernelPageSize
swapUsed := systemAttributesMap["__vmSwapUsage"]
freeMemory := systemAttributesMap["vmFreeCount"] * kernelPageSize
sysMemInfo := SystemMemData{
PerfDataBase: PerfDataBase{
Type: "sys_mem",
TimeStamp: timestamp,
},
AppMemory: appMemory,
UsedMemory: usedMemory,
WiredMemory: wiredMemory,
FreeMemory: freeMemory,
CachedFiles: cachedFiles,
Compressed: compressed,
SwapUsed: swapUsed,
}
memBytes, _ := json.Marshal(sysMemInfo)
c.chanSysMem <- memBytes
}
if c.options.SysDisk {
diskBytesRead := systemAttributesMap["diskBytesRead"]
diskBytesWritten := systemAttributesMap["diskBytesWritten"]
diskReadOps := systemAttributesMap["diskReadOps"]
diskWriteOps := systemAttributesMap["diskWriteOps"]
sysDiskInfo := SystemDiskData{
PerfDataBase: PerfDataBase{
Type: "sys_disk",
TimeStamp: timestamp,
},
DataRead: diskBytesRead,
DataWritten: diskBytesWritten,
ReadOps: diskReadOps,
WriteOps: diskWriteOps,
}
diskBytes, _ := json.Marshal(sysDiskInfo)
c.chanSysDisk <- diskBytes
}
if c.options.SysNetwork {
netBytesIn := systemAttributesMap["netBytesIn"]
netBytesOut := systemAttributesMap["netBytesOut"]
netPacketsIn := systemAttributesMap["netPacketsIn"]
netPacketsOut := systemAttributesMap["netPacketsOut"]
sysNetworkInfo := SystemNetworkData{
PerfDataBase: PerfDataBase{
Type: "sys_network",
TimeStamp: timestamp,
},
BytesIn: netBytesIn,
BytesOut: netBytesOut,
PacketsIn: netPacketsIn,
PacketsOut: netPacketsOut,
}
networkBytes, _ := json.Marshal(sysNetworkInfo)
c.chanSysNetwork <- networkBytes
}
}
type SystemCPUData struct {
PerfDataBase // system cpu
NiceLoad float64 `json:"nice_load"`
SystemLoad float64 `json:"system_load"`
TotalLoad float64 `json:"total_load"`
UserLoad float64 `json:"user_load"`
}
type SystemMemData struct {
PerfDataBase // mem
AppMemory int64 `json:"app_memory"`
FreeMemory int64 `json:"free_memory"`
UsedMemory int64 `json:"used_memory"`
WiredMemory int64 `json:"wired_memory"`
CachedFiles int64 `json:"cached_files"`
Compressed int64 `json:"compressed"`
SwapUsed int64 `json:"swap_used"`
}
type SystemDiskData struct {
PerfDataBase // disk
DataRead int64 `json:"data_read"`
DataWritten int64 `json:"data_written"`
ReadOps int64 `json:"reads_in"`
WriteOps int64 `json:"writes_out"`
}
type SystemNetworkData struct {
PerfDataBase // network
BytesIn int64 `json:"bytes_in"`
BytesOut int64 `json:"bytes_out"`
PacketsIn int64 `json:"packets_in"`
PacketsOut int64 `json:"packets_out"`
}
func (d *device) newPerfdNetworking(options *PerfOptions) (*perfdNetworking, error) {
instruments, err := d.newInstrumentsService()
if err != nil {
return nil, err
}
return &perfdNetworking{
perfdClient: perfdClient{
i: instruments,
options: options,
stop: make(chan struct{}),
},
chanNetwork: make(chan []byte, 10),
}, nil
}
type perfdNetworking struct {
perfdClient
chanNetwork chan []byte // network channel
}
func (c *perfdNetworking) Start() (data <-chan []byte, err error) {
if _, err = c.i.call(
instrumentsServiceNetworking,
"replayLastRecordedSession",
); err != nil {
return nil, err
}
if _, err = c.i.call(
instrumentsServiceNetworking,
"startMonitoring",
); err != nil {
return nil, err
}
ctx, cancel := context.WithCancel(context.TODO())
c.i.registerCallback("", func(m libimobiledevice.DTXMessageResult) {
select {
case <-ctx.Done():
c.i.call(instrumentsServiceNetworking, "stopMonitoring")
return
default:
c.parseNetworking(m.Obj)
}
})
c.cancel = cancel
outCh := make(chan []byte, 100)
go func() {
for {
select {
case <-c.stop:
c.cancel()
return
case networkBytes, ok := <-c.chanNetwork:
if ok {
outCh <- networkBytes
}
}
}
}()
return outCh, nil
}
func (c *perfdNetworking) Stop() {
close(c.stop)
}
func (c *perfdNetworking) parseNetworking(data interface{}) {
raw, ok := data.([]interface{})
if !ok || len(raw) != 2 {
fmt.Printf("invalid networking data: %v\n", data)
return
}
var netBytes []byte
msgType := raw[0].(uint64)
msgValue := raw[1].([]interface{})
if msgType == 0 {
// interface-detection
// ['InterfaceIndex', "Name"]
// e.g. [0, [14, 'en0']]
netData := NetworkDataInterfaceDetection{
PerfDataBase: PerfDataBase{
Type: "network-interface-detection",
TimeStamp: time.Now().Unix(),
},
InterfaceIndex: convert2Int64(msgValue[0]),
Name: msgValue[1].(string),
}
netBytes, _ = json.Marshal(netData)
} else if msgType == 1 {
// connection-detected
// ['LocalAddress', 'RemoteAddress', 'InterfaceIndex', 'Pid',
// 'RecvBufferSize', 'RecvBufferUsed', 'SerialNumber', 'Kind']
// e.g. [1 [[16 2 211 158 192 168 100 101 0 0 0 0 0 0 0 0]
// [16 2 0 53 183 221 253 100 0 0 0 0 0 0 0 0]
// 14 -2 786896 0 133 2]]
localAddr, err := parseSocketAddr(msgValue[0].([]byte))
if err != nil {
fmt.Printf("parse local socket address err: %v\n", err)
}
remoteAddr, err := parseSocketAddr(msgValue[1].([]byte))
if err != nil {
fmt.Printf("parse remote socket address err: %v\n", err)
}
netData := NetworkDataConnectionDetected{
PerfDataBase: PerfDataBase{
Type: "network-connection-detected",
TimeStamp: time.Now().Unix(),
},
LocalAddress: localAddr,
RemoteAddress: remoteAddr,
InterfaceIndex: convert2Int64(msgValue[2]),
Pid: convert2Int64(msgValue[3]),
RecvBufferSize: convert2Int64(msgValue[4]),
RecvBufferUsed: convert2Int64(msgValue[5]),
SerialNumber: convert2Int64(msgValue[6]),
Kind: convert2Int64(msgValue[7]),
}
netBytes, _ = json.Marshal(netData)
} else if msgType == 2 {
// connection-update
// ['RxPackets', 'RxBytes', 'TxPackets', 'TxBytes',
// 'RxDups', 'RxOOO', 'TxRetx', 'MinRTT', 'AvgRTT', 'ConnectionSerial']
// e.g. [2, [21, 1708, 22, 14119, 309, 0, 5830, 0.076125, 0.076125, 54, -1]]
netData := NetworkDataConnectionUpdate{
PerfDataBase: PerfDataBase{
Type: "network-connection-update",
TimeStamp: time.Now().Unix(),
},
RxBytes: convert2Int64(msgValue[0]),
RxPackets: convert2Int64(msgValue[1]),
TxBytes: convert2Int64(msgValue[2]),
TxPackets: convert2Int64(msgValue[3]),
}
if value, ok := msgValue[4].(uint64); ok {
netData.RxDups = int64(value)
}
if value, ok := msgValue[5].(uint64); ok {
netData.RxOOO = int64(value)
}
if value, ok := msgValue[6].(uint64); ok {
netData.TxRetx = int64(value)
}
if value, ok := msgValue[7].(uint64); ok {
netData.MinRTT = int64(value)
}
if value, ok := msgValue[8].(uint64); ok {
netData.AvgRTT = int64(value)
}
if value, ok := msgValue[9].(uint64); ok {
netData.ConnectionSerial = int64(value)
}
netBytes, _ = json.Marshal(netData)
}
c.chanNetwork <- netBytes
}
func parseSocketAddr(data []byte) (string, error) {
len := data[0] // length of address
_ = data[1] // family
port := binary.BigEndian.Uint16(data[2:4]) // port
// network, data[4:4+len]
if len == 0x10 {
// IPv4, 4 bytes
ip := net.IP(data[4:8])
return fmt.Sprintf("%s:%d", ip, port), nil
} else if len == 0x1c {
// IPv6, 16 bytes
ip := net.IP(data[4:20])
return fmt.Sprintf("%s:%d", ip, port), nil
}
return "", fmt.Errorf("invalid socket address: %v", data)
}
type PerfDataBase struct {
Type string `json:"type"`
TimeStamp int64 `json:"timestamp"`
Msg string `json:"msg,omitempty"` // message for invalid data
}
// network-interface-detection
type NetworkDataInterfaceDetection struct {
PerfDataBase
InterfaceIndex int64 `json:"interface_index"` // 0
Name string `json:"name"` // 1
}
// network-connection-detected
type NetworkDataConnectionDetected struct {
PerfDataBase
LocalAddress string `json:"local_address"` // 0
RemoteAddress string `json:"remote_address"` // 1
InterfaceIndex int64 `json:"interface_index"` // 2
Pid int64 `json:"pid"` // 3
RecvBufferSize int64 `json:"recv_buffer_size"` // 4
RecvBufferUsed int64 `json:"recv_buffer_used"` // 5
SerialNumber int64 `json:"serial_number"` // 6
Kind int64 `json:"kind"` // 7
}
// network-connection-update
type NetworkDataConnectionUpdate struct {
PerfDataBase
RxBytes int64 `json:"rx_bytes"` // 0
RxPackets int64 `json:"rx_packets"` // 1
TxBytes int64 `json:"tx_bytes"` // 2
TxPackets int64 `json:"tx_packets"` // 3
RxDups int64 `json:"rx_dups,omitempty"` // 4
RxOOO int64 `json:"rx_000,omitempty"` // 5
TxRetx int64 `json:"tx_retx,omitempty"` // 6
MinRTT int64 `json:"min_rtt,omitempty"` // 7
AvgRTT int64 `json:"avg_rtt,omitempty"` // 8
ConnectionSerial int64 `json:"connection_serial"` // 9
}
func (d *device) newPerfdGraphicsOpengl(options *PerfOptions) (*perfdGraphicsOpengl, error) {
instruments, err := d.newInstrumentsService()
if err != nil {
return nil, err
}
return &perfdGraphicsOpengl{
perfdClient: perfdClient{
i: instruments,
options: options,
stop: make(chan struct{}),
},
chanGPU: make(chan []byte, 10),
chanFPS: make(chan []byte, 10),
}, nil
}
type perfdGraphicsOpengl struct {
perfdClient
chanGPU chan []byte // gpu channel
chanFPS chan []byte // fps channel
}
func (c *perfdGraphicsOpengl) Start() (data <-chan []byte, err error) {
if _, err = c.i.call(
instrumentsServiceGraphicsOpengl,
"setSamplingRate:",
float64(c.options.OutputInterval)/100, // FIXME: unable to set sampling rate, always 1.0
); err != nil {
return nil, err
}
if _, err = c.i.call(
instrumentsServiceGraphicsOpengl,
"startSamplingAtTimeInterval:",
0,
); err != nil {
return nil, err
}
ctx, cancel := context.WithCancel(context.TODO())
c.i.registerCallback("", func(m libimobiledevice.DTXMessageResult) {
select {
case <-ctx.Done():
c.i.call(instrumentsServiceGraphicsOpengl, "stopSampling")
return
default:
c.parseData(m.Obj)
}
})
c.cancel = cancel
outCh := make(chan []byte, 100)
go func() {
for {
select {
case <-c.stop:
c.cancel()
return
case gpuBytes, ok := <-c.chanGPU:
if ok {
outCh <- gpuBytes
}
case fpsBytes, ok := <-c.chanFPS:
if ok {
outCh <- fpsBytes
}
}
}
}()
return outCh, nil
}
func (c *perfdGraphicsOpengl) Stop() {
close(c.stop)
}
func (c *perfdGraphicsOpengl) parseData(data interface{}) {
// data example:
// map[
// Alloc system memory:50167808
// Allocated PB Size:1179648
// CoreAnimationFramesPerSecond:0 // fps from GPU
// Device Utilization %:0 // device
// IOGLBundleName:Built-In
// In use system memory:10633216
// Renderer Utilization %:0 // renderer
// SplitSceneCount:0
// TiledSceneBytes:0
// Tiler Utilization %:0 // tiler
// XRVideoCardRunTimeStamp:1010679
// recoveryCount:0
// ]
gpuInfo := GPUData{
PerfDataBase: PerfDataBase{
Type: "gpu",
TimeStamp: time.Now().Unix(),
},
}
fpsInfo := FPSData{
PerfDataBase: PerfDataBase{
Type: "fps",
TimeStamp: time.Now().Unix(),
},
}
defer func() {
if c.options.gpu {
gpuBytes, _ := json.Marshal(gpuInfo)
c.chanGPU <- gpuBytes
}
if c.options.FPS {
fpsBytes, _ := json.Marshal(fpsInfo)
c.chanFPS <- fpsBytes
}
}()
raw, ok := data.(map[string]interface{})
if !ok {
gpuInfo.Msg = fmt.Sprintf("invalid graphics.opengl data: %v", data)
return
}
// gpu
gpuInfo.DeviceUtilization = convert2Int64(raw["Device Utilization %"])
gpuInfo.TilerUtilization = convert2Int64(raw["Tiler Utilization %"])
gpuInfo.RendererUtilization = convert2Int64(raw["Renderer Utilization %"])
// fps
fpsInfo.FPS = int(convert2Int64(raw["CoreAnimationFramesPerSecond"]))
}
type GPUData struct {
PerfDataBase // gpu
TilerUtilization int64 `json:"tiler_utilization"` // 处理顶点的 GPU 时间占比
DeviceUtilization int64 `json:"device_utilization"` // 设备利用率
RendererUtilization int64 `json:"renderer_utilization"` // 渲染器利用率
}
type FPSData struct {
PerfDataBase // fps
FPS int `json:"fps"`
}
func convert2Int64(num interface{}) int64 {
switch value := num.(type) {
case int64:
return value
case uint64:
return int64(value)
case uint32:
return int64(value)
case uint16:
return int64(value)
case uint8:
return int64(value)
case uint:
return int64(value)
}
fmt.Printf("convert2Int64 failed: %v, %T\n", num, num)
return -1
}
func containString(ss []string, s string) bool {
for _, v := range ss {
if s == v {
return true
}
}
return false
}