// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. // If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. // Copyright (C) LibreHardwareMonitor and Contributors. // Partial Copyright (C) Michael Möller and Contributors. // All Rights Reserved. using System; using System.Collections.Generic; using System.Globalization; using System.Text; namespace LibreHardwareMonitor.Hardware.Cpu; internal class CpuGroup : IGroup { private readonly List _hardware = new(); private readonly CpuId[][][] _threads; public CpuGroup(ISettings settings) { CpuId[][] processorThreads = GetProcessorThreads(); _threads = new CpuId[processorThreads.Length][][]; int index = 0; foreach (CpuId[] threads in processorThreads) { if (threads.Length == 0) continue; CpuId[][] coreThreads = GroupThreadsByCore(threads); _threads[index] = coreThreads; switch (threads[0].Vendor) { case Vendor.Intel: _hardware.Add(new IntelCpu(index, coreThreads, settings)); break; case Vendor.AMD: switch (threads[0].Family) { case 0x0F: _hardware.Add(new Amd0FCpu(index, coreThreads, settings)); break; case 0x10: case 0x11: case 0x12: case 0x14: case 0x15: case 0x16: _hardware.Add(new Amd10Cpu(index, coreThreads, settings)); break; case 0x17: case 0x19: case 0x1A: _hardware.Add(new Amd17Cpu(index, coreThreads, settings)); break; default: _hardware.Add(new GenericCpu(index, coreThreads, settings)); break; } break; default: _hardware.Add(new GenericCpu(index, coreThreads, settings)); break; } index++; } } public IReadOnlyList Hardware => _hardware; public string GetReport() { if (_threads == null) return null; StringBuilder r = new(); r.AppendLine("CPUID"); r.AppendLine(); for (int i = 0; i < _threads.Length; i++) { r.AppendLine("Processor " + i); r.AppendLine(); r.AppendFormat("Processor Vendor: {0}{1}", _threads[i][0][0].Vendor, Environment.NewLine); r.AppendFormat("Processor Brand: {0}{1}", _threads[i][0][0].BrandString, Environment.NewLine); r.AppendFormat("Family: 0x{0}{1}", _threads[i][0][0].Family.ToString("X", CultureInfo.InvariantCulture), Environment.NewLine); r.AppendFormat("Model: 0x{0}{1}", _threads[i][0][0].Model.ToString("X", CultureInfo.InvariantCulture), Environment.NewLine); r.AppendFormat("Stepping: 0x{0}{1}", _threads[i][0][0].Stepping.ToString("X", CultureInfo.InvariantCulture), Environment.NewLine); r.AppendLine(); r.AppendLine("CPUID Return Values"); r.AppendLine(); for (int j = 0; j < _threads[i].Length; j++) { for (int k = 0; k < _threads[i][j].Length; k++) { r.AppendLine(" CPU Group: " + _threads[i][j][k].Group); r.AppendLine(" CPU Thread: " + _threads[i][j][k].Thread); r.AppendLine(" APIC ID: " + _threads[i][j][k].ApicId); r.AppendLine(" Processor ID: " + _threads[i][j][k].ProcessorId); r.AppendLine(" Core ID: " + _threads[i][j][k].CoreId); r.AppendLine(" Thread ID: " + _threads[i][j][k].ThreadId); r.AppendLine(); r.AppendLine(" Function EAX EBX ECX EDX"); AppendCpuidData(r, _threads[i][j][k].Data, CpuId.CPUID_0); AppendCpuidData(r, _threads[i][j][k].ExtData, CpuId.CPUID_EXT); r.AppendLine(); } } } return r.ToString(); } public void Close() { foreach (GenericCpu cpu in _hardware) { cpu.Close(); } } private static CpuId[][] GetProcessorThreads() { List threads = new(); for (int i = 0; i < ThreadAffinity.ProcessorGroupCount; i++) { for (int j = 0; j < 192; j++) { try { var cpuid = CpuId.Get(i, j); if (cpuid != null) threads.Add(cpuid); } catch (ArgumentOutOfRangeException) { // Continue... } } } SortedDictionary> processors = new(); foreach (CpuId thread in threads) { processors.TryGetValue(thread.ProcessorId, out List list); if (list == null) { list = new List(); processors.Add(thread.ProcessorId, list); } list.Add(thread); } CpuId[][] processorThreads = new CpuId[processors.Count][]; int index = 0; foreach (List list in processors.Values) { processorThreads[index] = list.ToArray(); index++; } return processorThreads; } private static CpuId[][] GroupThreadsByCore(IEnumerable threads) { SortedDictionary> cores = new(); foreach (CpuId thread in threads) { cores.TryGetValue(thread.CoreId, out List coreList); if (coreList == null) { coreList = new List(); cores.Add(thread.CoreId, coreList); } coreList.Add(thread); } CpuId[][] coreThreads = new CpuId[cores.Count][]; int index = 0; foreach (List list in cores.Values) { coreThreads[index] = list.ToArray(); index++; } return coreThreads; } private static void AppendCpuidData(StringBuilder r, uint[,] data, uint offset) { for (int i = 0; i < data.GetLength(0); i++) { r.Append(" "); r.Append((i + offset).ToString("X8", CultureInfo.InvariantCulture)); for (int j = 0; j < 4; j++) { r.Append(" "); r.Append(data[i, j].ToString("X8", CultureInfo.InvariantCulture)); } r.AppendLine(); } } }