//go:build linux
// +build linux

package cpu

import (
	
	
	
	
	
	

	

	
)

var ClocksPerSec = float64(100)

var armModelToModelName = map[uint64]string{
	0x810: "ARM810",
	0x920: "ARM920",
	0x922: "ARM922",
	0x926: "ARM926",
	0x940: "ARM940",
	0x946: "ARM946",
	0x966: "ARM966",
	0xa20: "ARM1020",
	0xa22: "ARM1022",
	0xa26: "ARM1026",
	0xb02: "ARM11 MPCore",
	0xb36: "ARM1136",
	0xb56: "ARM1156",
	0xb76: "ARM1176",
	0xc05: "Cortex-A5",
	0xc07: "Cortex-A7",
	0xc08: "Cortex-A8",
	0xc09: "Cortex-A9",
	0xc0d: "Cortex-A17",
	0xc0f: "Cortex-A15",
	0xc0e: "Cortex-A17",
	0xc14: "Cortex-R4",
	0xc15: "Cortex-R5",
	0xc17: "Cortex-R7",
	0xc18: "Cortex-R8",
	0xc20: "Cortex-M0",
	0xc21: "Cortex-M1",
	0xc23: "Cortex-M3",
	0xc24: "Cortex-M4",
	0xc27: "Cortex-M7",
	0xc60: "Cortex-M0+",
	0xd01: "Cortex-A32",
	0xd02: "Cortex-A34",
	0xd03: "Cortex-A53",
	0xd04: "Cortex-A35",
	0xd05: "Cortex-A55",
	0xd06: "Cortex-A65",
	0xd07: "Cortex-A57",
	0xd08: "Cortex-A72",
	0xd09: "Cortex-A73",
	0xd0a: "Cortex-A75",
	0xd0b: "Cortex-A76",
	0xd0c: "Neoverse-N1",
	0xd0d: "Cortex-A77",
	0xd0e: "Cortex-A76AE",
	0xd13: "Cortex-R52",
	0xd20: "Cortex-M23",
	0xd21: "Cortex-M33",
	0xd40: "Neoverse-V1",
	0xd41: "Cortex-A78",
	0xd42: "Cortex-A78AE",
	0xd43: "Cortex-A65AE",
	0xd44: "Cortex-X1",
	0xd46: "Cortex-A510",
	0xd47: "Cortex-A710",
	0xd48: "Cortex-X2",
	0xd49: "Neoverse-N2",
	0xd4a: "Neoverse-E1",
	0xd4b: "Cortex-A78C",
	0xd4c: "Cortex-X1C",
	0xd4d: "Cortex-A715",
	0xd4e: "Cortex-X3",
}

func init() {
	,  := sysconf.Sysconf(sysconf.SC_CLK_TCK)
	// ignore errors
	if  == nil {
		ClocksPerSec = float64()
	}
}

func ( bool) ([]TimesStat, error) {
	return TimesWithContext(context.Background(), )
}

func ( context.Context,  bool) ([]TimesStat, error) {
	 := common.HostProcWithContext(, "stat")
	 := []string{}
	if  {
		,  := common.ReadLines()
		if  != nil || len() < 2 {
			return []TimesStat{}, nil
		}
		for ,  := range [1:] {
			if !strings.HasPrefix(, "cpu") {
				break
			}
			 = append(, )
		}
	} else {
		, _ = common.ReadLinesOffsetN(, 0, 1)
	}

	 := make([]TimesStat, 0, len())

	for ,  := range  {
		,  := parseStatLine()
		if  != nil {
			continue
		}
		 = append(, *)

	}
	return , nil
}

func sysCPUPath( context.Context,  int32,  string) string {
	return common.HostSysWithContext(, fmt.Sprintf("devices/system/cpu/cpu%d", ), )
}

func finishCPUInfo( context.Context,  *InfoStat) {
	var  []string
	var  error
	var  float64

	if len(.CoreID) == 0 {
		,  = common.ReadLines(sysCPUPath(, .CPU, "topology/core_id"))
		if  == nil {
			.CoreID = [0]
		}
	}

	// override the value of c.Mhz with cpufreq/cpuinfo_max_freq regardless
	// of the value from /proc/cpuinfo because we want to report the maximum
	// clock-speed of the CPU for c.Mhz, matching the behaviour of Windows
	,  = common.ReadLines(sysCPUPath(, .CPU, "cpufreq/cpuinfo_max_freq"))
	// if we encounter errors below such as there are no cpuinfo_max_freq file,
	// we just ignore. so let Mhz is 0.
	if  != nil || len() == 0 {
		return
	}
	,  = strconv.ParseFloat([0], 64)
	if  != nil {
		return
	}
	.Mhz =  / 1000.0 // value is in kHz
	if .Mhz > 9999 {
		.Mhz = .Mhz / 1000.0 // value in Hz
	}
}

// CPUInfo on linux will return 1 item per physical thread.
//
// CPUs have three levels of counting: sockets, cores, threads.
// Cores with HyperThreading count as having 2 threads per core.
// Sockets often come with many physical CPU cores.
// For example a single socket board with two cores each with HT will
// return 4 CPUInfoStat structs on Linux and the "Cores" field set to 1.
func () ([]InfoStat, error) {
	return InfoWithContext(context.Background())
}

func ( context.Context) ([]InfoStat, error) {
	 := common.HostProcWithContext(, "cpuinfo")
	,  := common.ReadLines()

	var  []InfoStat
	var  string

	 := InfoStat{CPU: -1, Cores: 1}
	for ,  := range  {
		 := strings.Split(, ":")
		if len() < 2 {
			continue
		}
		 := strings.TrimSpace([0])
		 := strings.TrimSpace([1])

		switch  {
		case "Processor":
			 = 
		case "processor", "cpu number":
			if .CPU >= 0 {
				finishCPUInfo(, &)
				 = append(, )
			}
			 = InfoStat{Cores: 1, ModelName: }
			,  := strconv.ParseInt(, 10, 64)
			if  != nil {
				return , 
			}
			.CPU = int32()
		case "vendorId", "vendor_id":
			.VendorID = 
			if strings.Contains(, "S390") {
				 = "S390"
			}
		case "CPU implementer":
			if ,  := strconv.ParseUint(, 0, 8);  == nil {
				switch  {
				case 0x41:
					.VendorID = "ARM"
				case 0x42:
					.VendorID = "Broadcom"
				case 0x43:
					.VendorID = "Cavium"
				case 0x44:
					.VendorID = "DEC"
				case 0x46:
					.VendorID = "Fujitsu"
				case 0x48:
					.VendorID = "HiSilicon"
				case 0x49:
					.VendorID = "Infineon"
				case 0x4d:
					.VendorID = "Motorola/Freescale"
				case 0x4e:
					.VendorID = "NVIDIA"
				case 0x50:
					.VendorID = "APM"
				case 0x51:
					.VendorID = "Qualcomm"
				case 0x56:
					.VendorID = "Marvell"
				case 0x61:
					.VendorID = "Apple"
				case 0x69:
					.VendorID = "Intel"
				case 0xc0:
					.VendorID = "Ampere"
				}
			}
		case "cpu family":
			.Family = 
		case "model", "CPU part":
			.Model = 
			// if CPU is arm based, model name is found via model number. refer to: arch/arm64/kernel/cpuinfo.c
			if .VendorID == "ARM" {
				if ,  := strconv.ParseUint(.Model, 0, 16);  == nil {
					,  := armModelToModelName[]
					if  {
						.ModelName = 
					} else {
						.ModelName = "Undefined"
					}
				}
			}
		case "Model Name", "model name", "cpu":
			.ModelName = 
			if strings.Contains(, "POWER") {
				.Model = strings.Split(, " ")[0]
				.Family = "POWER"
				.VendorID = "IBM"
			}
		case "stepping", "revision", "CPU revision":
			 := 

			if  == "revision" {
				 = strings.Split(, ".")[0]
			}

			,  := strconv.ParseInt(, 10, 64)
			if  != nil {
				return , 
			}
			.Stepping = int32()
		case "cpu MHz", "clock", "cpu MHz dynamic":
			// treat this as the fallback value, thus we ignore error
			if ,  := strconv.ParseFloat(strings.Replace(, "MHz", "", 1), 64);  == nil {
				.Mhz = 
			}
		case "cache size":
			,  := strconv.ParseInt(strings.Replace(, " KB", "", 1), 10, 64)
			if  != nil {
				return , 
			}
			.CacheSize = int32()
		case "physical id":
			.PhysicalID = 
		case "core id":
			.CoreID = 
		case "flags", "Features":
			.Flags = strings.FieldsFunc(, func( rune) bool {
				return  == ',' ||  == ' '
			})
		case "microcode":
			.Microcode = 
		}
	}
	if .CPU >= 0 {
		finishCPUInfo(, &)
		 = append(, )
	}
	return , nil
}

func parseStatLine( string) (*TimesStat, error) {
	 := strings.Fields()

	if len() < 8 {
		return nil, errors.New("stat does not contain cpu info")
	}

	if !strings.HasPrefix([0], "cpu") {
		return nil, errors.New("not contain cpu")
	}

	 := [0]
	if  == "cpu" {
		 = "cpu-total"
	}
	,  := strconv.ParseFloat([1], 64)
	if  != nil {
		return nil, 
	}
	,  := strconv.ParseFloat([2], 64)
	if  != nil {
		return nil, 
	}
	,  := strconv.ParseFloat([3], 64)
	if  != nil {
		return nil, 
	}
	,  := strconv.ParseFloat([4], 64)
	if  != nil {
		return nil, 
	}
	,  := strconv.ParseFloat([5], 64)
	if  != nil {
		return nil, 
	}
	,  := strconv.ParseFloat([6], 64)
	if  != nil {
		return nil, 
	}
	,  := strconv.ParseFloat([7], 64)
	if  != nil {
		return nil, 
	}

	 := &TimesStat{
		CPU:     ,
		User:     / ClocksPerSec,
		Nice:     / ClocksPerSec,
		System:   / ClocksPerSec,
		Idle:     / ClocksPerSec,
		Iowait:   / ClocksPerSec,
		Irq:      / ClocksPerSec,
		Softirq:  / ClocksPerSec,
	}
	if len() > 8 { // Linux >= 2.6.11
		,  := strconv.ParseFloat([8], 64)
		if  != nil {
			return nil, 
		}
		.Steal =  / ClocksPerSec
	}
	if len() > 9 { // Linux >= 2.6.24
		,  := strconv.ParseFloat([9], 64)
		if  != nil {
			return nil, 
		}
		.Guest =  / ClocksPerSec
	}
	if len() > 10 { // Linux >= 3.2.0
		,  := strconv.ParseFloat([10], 64)
		if  != nil {
			return nil, 
		}
		.GuestNice =  / ClocksPerSec
	}

	return , nil
}

func ( context.Context,  bool) (int, error) {
	if  {
		 := 0
		// https://github.com/giampaolo/psutil/blob/d01a9eaa35a8aadf6c519839e987a49d8be2d891/psutil/_pslinux.py#L599
		 := common.HostProcWithContext(, "cpuinfo")
		,  := common.ReadLines()
		if  == nil {
			for ,  := range  {
				 = strings.ToLower()
				if strings.HasPrefix(, "processor") {
					_,  = strconv.Atoi(strings.TrimSpace([strings.IndexByte(, ':')+1:]))
					if  == nil {
						++
					}
				}
			}
		}
		if  == 0 {
			 := common.HostProcWithContext(, "stat")
			,  = common.ReadLines()
			if  != nil {
				return 0, 
			}
			for ,  := range  {
				if len() >= 4 && strings.HasPrefix(, "cpu") && '0' <= [3] && [3] <= '9' { // `^cpu\d` regexp matching
					++
				}
			}
		}
		return , nil
	}
	// physical cores
	// https://github.com/giampaolo/psutil/blob/8415355c8badc9c94418b19bdf26e622f06f0cce/psutil/_pslinux.py#L615-L628
	 := make(map[string]bool)
	// These 2 files are the same but */core_cpus_list is newer while */thread_siblings_list is deprecated and may disappear in the future.
	// https://www.kernel.org/doc/Documentation/admin-guide/cputopology.rst
	// https://github.com/giampaolo/psutil/pull/1727#issuecomment-707624964
	// https://lkml.org/lkml/2019/2/26/41
	for ,  := range []string{"devices/system/cpu/cpu[0-9]*/topology/core_cpus_list", "devices/system/cpu/cpu[0-9]*/topology/thread_siblings_list"} {
		if ,  := filepath.Glob(common.HostSysWithContext(, ));  == nil {
			for ,  := range  {
				,  := common.ReadLines()
				if  != nil || len() != 1 {
					continue
				}
				[[0]] = true
			}
			 := len()
			if  != 0 {
				return , nil
			}
		}
	}
	// https://github.com/giampaolo/psutil/blob/122174a10b75c9beebe15f6c07dcf3afbe3b120d/psutil/_pslinux.py#L631-L652
	 := common.HostProcWithContext(, "cpuinfo")
	,  := common.ReadLines()
	if  != nil {
		return 0, 
	}
	 := make(map[int]int)
	 := make(map[string]int)
	for ,  := range  {
		 = strings.ToLower(strings.TrimSpace())
		if  == "" {
			// new section
			,  := ["physical id"]
			,  := ["cpu cores"]
			if  &&  {
				[] = 
			}
			 = make(map[string]int)
			continue
		}
		 := strings.Split(, ":")
		if len() < 2 {
			continue
		}
		[0] = strings.TrimSpace([0])
		if [0] == "physical id" || [0] == "cpu cores" {
			,  := strconv.Atoi(strings.TrimSpace([1]))
			if  != nil {
				continue
			}
			[[0]] = 
		}
	}
	 := 0
	for ,  := range  {
		 += 
	}
	return , nil
}