aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rwxr-xr-xinxi2568
-rw-r--r--inxi.1100
-rw-r--r--inxi.changelog196
3 files changed, 1692 insertions, 1172 deletions
diff --git a/inxi b/inxi
index bd3e545..b400b92 100755
--- a/inxi
+++ b/inxi
@@ -49,8 +49,8 @@ use POSIX qw(ceil uname strftime ttyname);
## INXI INFO ##
my $self_name='inxi';
-my $self_version='3.3.35';
-my $self_date='2024-06-18';
+my $self_version='3.3.36';
+my $self_date='2024-09-04';
my $self_patch='00';
## END INXI INFO ##
@@ -208,7 +208,7 @@ sub initialize {
set_display_size();
}
-## CheckTools
+## CheckTools ##
{
package CheckTools;
my (%commands);
@@ -901,7 +901,7 @@ sub set_colors {
eval $end if $b_log;
}
-## SelectColors
+## SelectColors ##
{
package SelectColors;
my (@data,%configs,%status);
@@ -1546,7 +1546,7 @@ sub set_debugger {
}
}
-## SystemDebugger
+## SystemDebugger ##
{
package SystemDebugger;
my $option = 'main';
@@ -2018,6 +2018,7 @@ sub display_data {
['swaymsg','-t get_tree'],
['swaymsg','-t get_workspaces -p'],
['swaymsg','-t get_workspaces -r'],
+ ['switcherooctl','list'],
['twin','--version'], # TDE
['vainfo',''],
['vdpauinfo',''],
@@ -2975,6 +2976,8 @@ sub error_handler {
$errno=60; "Downloader program $two could not be located on your system." }
elsif ($err eq 'missing-perl-downloader'){
$errno=61; $b_recommends=1; "Perl downloader missing required module." }
+ elsif ($err eq 'no-downloader'){
+ $errno=62; $b_recommends=1; "No downloader program located on your system." }
## FTP
elsif ($err eq 'ftp-bad-path'){
$errno=70; "Unable to locate for FTP upload file:\n$one" }
@@ -3018,7 +3021,7 @@ sub error_defaults {
#### RECOMMENDS
#### -------------------------------------------------------------------
-## CheckRecommends
+## CheckRecommends ##
{
package CheckRecommends;
my ($item_data,@modules,@pms);
@@ -3108,15 +3111,15 @@ sub check_items {
elsif ($type eq 'recommended system programs'){
if ($bsd_type){
@data = qw(camcontrol dig disklabel dmidecode doas fdisk file glabel gpart
- ifconfig ipmi-sensors ipmitool pciconfig pcidump pcictl smartctl sudo
+ ifconfig ipmi-sensors ipmitool pciconfig pcidump pcictl ps smartctl sudo
sysctl tree upower uptime usbconfig usbdevs);
$info_os = 'info-bsd';
}
else {
@data = qw(blockdev bt-adapter btmgmt dig dmidecode doas fdisk file
fruid_print hciconfig hddtemp ifconfig ip ipmitool ipmi-sensors lsblk
- lsusb lvs mdadm modinfo runlevel sensors smartctl strings sudo tree
- udevadm upower uptime);
+ lspci lsusb lvs mdadm modinfo ps runlevel sensors smartctl strings sudo
+ tree udevadm upower uptime);
}
$b_program = 1;
$item = 'Program';
@@ -3480,7 +3483,7 @@ sub set_item_data {
'rpm' => 'hddtemp',
},
'ifconfig' => {
- 'info' => '-i ip LAN (deprecated)',
+ 'info' => '-i ip LAN (deprecated, ip preferred)',
'info-bsd' => '-i ip LAN',
'apt' => 'net-tools',
'pacman' => 'net-tools',
@@ -3519,22 +3522,30 @@ sub set_item_data {
'pkgtool' => 'util-linux',
'rpm' => 'util-linux-ng',
},
- 'lvs' => {
- 'info' => '-L LVM data',
+ 'lspci' => {
+ 'info' => '-A,-E,-G,-N,-R PCI Device data (/sys supplies much)',
'info-bsd' => '',
- 'apt' => 'lvm2',
- 'pacman' => 'lvm2',
- 'pkgtool' => 'lvm2',
- 'rpm' => 'lvm2',
+ 'apt' => 'pciutils',
+ 'pacman' => 'pciutils',
+ 'pkgtool' => 'pciutils',
+ 'rpm' => 'pciutils',
},
'lsusb' => {
- 'info' => '-A usb audio; -J (optional); -N usb networking',
+ 'info' => '-A,-E,-G,-J,-N USB Device data (/sys supplies much)',
'info-bsd' => '',
'apt' => 'usbutils',
'pacman' => 'usbutils',
'pkgtool' => 'usbutils',
'rpm' => 'usbutils',
},
+ 'lvs' => {
+ 'info' => '-L LVM data',
+ 'info-bsd' => '',
+ 'apt' => 'lvm2',
+ 'pacman' => 'lvm2',
+ 'pkgtool' => 'lvm2',
+ 'rpm' => 'lvm2',
+ },
'mdadm' => {
'info' => '-Ra advanced mdraid data',
'info-bsd' => '',
@@ -3563,6 +3574,14 @@ sub set_item_data {
'info' => '',
'info-bsd' => '-A,-E,-G,-N pci devices (OpenBSD+derived, doas/su)',
},
+ 'ps' => {
+ 'info' => '-G,-I,-n,-S,-t process/programs',
+ 'info-bsd' => '-G,-I,-n,-S,-t process/programs',
+ 'apt' => 'procps',
+ 'pacman' => 'procps',
+ 'pkgtool' => 'procps',
+ 'rpm' => 'procps',
+ },
'runlevel' => {
'info' => '-I fallback to Perl',
'info-bsd' => '',
@@ -4201,6 +4220,10 @@ sub update_me {
error_handler('not-writable', "$self_name", '');
}
$output .= "Starting $self_name self updater.\n";
+ if (!$dl{'dl'}){
+ print $output;
+ main::error_handler('no-downloader');
+ }
$output .= "Using $dl{'dl'} as downloader.\n";
$output .= "Currently running $self_name version number: $self_version\n";
$output .= "Current version patch number: $self_patch\n";
@@ -4370,7 +4393,7 @@ sub set_version_data {
#### OPTIONS HANDLER / VERSION
########################################################################
-## OptionsHandler
+## OptionsHandler ##
{
package OptionsHandler;
# Note: used %trigger here, but perl 5.008 had issues, so mmoved to global.
@@ -5756,9 +5779,9 @@ sub show_options {
found); list of installed tools for servers."],
['2', '-C', '', "If available: microarchitecture level (64 bit AMD/Intel
only).CPU generation, process node, built years; CPU socket type, base/boost
- speeds (dmidecode+root/sudo/doas required); Full topology line, with cores,
- threads, threads per core, granular cache data, smt status; CPU
- vulnerabilities (bugs); family, model-id, stepping - format: hex (decimal)
+ speeds (dmidecode+root/sudo/doas required); Full topology line, with dies,
+ clusters, cores, threads, threads per core, granular cache data, smt status;
+ CPU vulnerabilities (bugs); family, model-id, stepping - format: hex (decimal)
if greater than 9; microcode format: hex."],
['2', '-d,-D', '', "If available: logical and physical block sizes; drive
family; maj:min; USB mode (if found); USB drive specifics; SMART report."],
@@ -5837,7 +5860,7 @@ sub show_options {
['2', '44', '', "Bypass Curl, Fetch, and Wget as downloader options. Forces
Perl if HTTP::Tiny present."],
['1', '', '--bt-tool', "[bt-adapter btmgmt hciconfig rfkill] Force use of
- given tool forbluetooth report. Or use --force [tool]."],
+ given tool for bluetooth report. Or use --force [tool]."],
['1', '', '--dig', "Overrides configuration item NO_DIG (resets to default)."],
['1', '', '--display', "[:[0-9]] Try to get display data out of X (default:
display 0)."],
@@ -5999,7 +6022,7 @@ sub show_version {
#### STARTUP DATA
########################################################################
-## StartClient
+## StartClient ##
{
package StartClient;
# use warnings;
@@ -6179,7 +6202,7 @@ sub get_client_version {
# show no tty are actually IRC. tmux is not a vt, but runs inside one
if (!$client{'name-print'}){
my $wl_terms = 'alacritty|altyo|\bate\b|black-screen|conhost|doas|evilvte|';
- $wl_terms .= 'foot|germinal|guake|havoc|hyper|kate|kitty|kmscon|konsole|';
+ $wl_terms .= 'foot|germinal|guake|havoc|hyper|kate|kgx|kitty|kmscon|konsole|';
$wl_terms .= 'login|macwise|minicom|putty|rxvt|sakura|securecrt|';
$wl_terms .= 'shellinabox|^st$|sudo|term|tilda|tilix|tmux|tym|wayst|xiki|';
$wl_terms .= 'yaft|yakuake|\bzoc\b';
@@ -6485,25 +6508,23 @@ sub filter {
}
# Note, let the print logic handle N/A cases
+# args: 0: type; 1: string, by reference; 2: test value for system
sub filter_partition {
- my ($source,$string,$type) = @_;
- return $string if !$string || $string eq 'N/A';
- if ($source eq 'system'){
- my $test = ($type eq 'label') ? '=LABEL=': '=UUID=';
- $string =~ s/$test[^\s]+/$test$filter_string/g;
+ return if !$_[1] || !${$_[1]} || ${$_[1]} eq 'N/A';
+ if ($_[0]eq 'system'){
+ ${$_[1]} =~ s/${_[2]}[^\s]+/${_[2]}$filter_string/g;
}
else {
- $string = $filter_string;
+ ${$_[1]} = $filter_string;
}
- return $string;
}
+# note these are tested before being sent so no need to test for null
+# args: 0: string to filter. by reference
sub filter_pci_long {
- my ($string) = @_;
- if ($string =~ /\[AMD(\/ATI)?\]/){
- $string =~ s/Advanced\sMicro\sDevices\s\[AMD(\/ATI)?\]/AMD/;
+ if ($_[0] =~ /\[AMD(\/ATI)?\]/){
+ ${$_[0]} =~ s/Advanced\sMicro\sDevices\s\[AMD(\/ATI)?\]/AMD/;
}
- return $string;
}
# args: 0: list of values. Return the first one that is defined.
@@ -7327,7 +7348,7 @@ sub print_line {
#### ITEM GENERATORS
#### -------------------------------------------------------------------
-## AudioItem
+## AudioItem ##
{
package AudioItem;
@@ -7377,7 +7398,7 @@ sub device_output {
$device = ($device) ? main::clean_pci($device,'output') : 'N/A';
# have seen absurdly verbose card descriptions, with non related data etc
if (length($device) > 85 || $size{'max-cols'} < 110){
- $device = main::filter_pci_long($device);
+ main::filter_pci_long(\$device);
}
push(@$rows, {
main::key($num++,1,1,'Device') => $device,
@@ -7906,7 +7927,7 @@ sub sound_tools {
}
}
-## BatteryItem
+## BatteryItem ##
{
package BatteryItem;
my (@upower_items,$b_upower,$upower);
@@ -8453,7 +8474,7 @@ sub upower_data {
}
}
-## BluetoothItem
+## BluetoothItem ##
{
package BluetoothItem;
my ($b_bluetooth,$b_hci_error,$b_hci,$b_rfk,$b_service);
@@ -8509,7 +8530,7 @@ sub device_output {
$device = ($device) ? main::clean_pci($device,'output') : 'N/A';
# have seen absurdly verbose card descriptions, with non related data etc
if (length($device) > 85 || $size{'max-cols'} < 110){
- $device = main::filter_pci_long($device);
+ main::filter_pci_long(\$device);
}
push(@$rows, {
main::key($num++,1,1,'Device') => $device,
@@ -9022,7 +9043,7 @@ sub bluetooth_version {
}
}
-## CpuItem
+## CpuItem ##
{
package CpuItem;
my (%fake_data,$type);
@@ -9062,9 +9083,9 @@ sub full_output {
my ($key1,$val1) = ('','');
if ($alerts{'sysctl'}){
if ($alerts{'sysctl'}->{'action'} eq 'use'){
-# $key1 = 'Status';
-# $val1 = main::message('dev');
- $cpu = sysctl_data();
+ # $key1 = 'Status';
+ # $val1 = main::message('dev');
+ $cpu = cpu_sysctl_data();
}
else {
$key1 = ucfirst($alerts{'sysctl'}->{'action'});
@@ -9088,7 +9109,7 @@ sub full_output {
my $counter = (%system_cpus && scalar keys %system_cpus > 1) ? '-' : '';
foreach my $key (keys %system_cpus){
$counter = '-' . $i++ if $counter;
- $rows->[$j]{main::key($num++,0,2,'variant'.$counter)} = $key;
+ $rows->[$j]{main::key($num++,0,2,'variant' . $counter)} = $key;
}
}
if ($b_admin && $properties->{'socket'}){
@@ -9175,7 +9196,16 @@ sub full_output {
}
my $x = ($size{'max-cols'} == 1 || $output_type ne 'screen') ? '' : 'x';
$rows->[$j]{main::key($num++,0,$id,'cpus')} = $topo->{'cpus'} . $x;
+ if ($topo->{'dies-count'}){
+ $rows->[$j]{main::key($num++,0,$id+1,'dies')} = $topo->{'dies-count'};
+ }
+ if ($topo->{'clusters'}){
+ $rows->[$j]{main::key($num++,0,$id+1,'clusters')} = $topo->{'clusters'};
+ }
$rows->[$j]{main::key($num++,1,$id+1,'cores')} = $topo->{'cores'};
+ if ($topo->{'threads'}){
+ $rows->[$j]{main::key($num++,0,$id+1,'threads')} = $topo->{'threads'};
+ }
if ($topo->{'cores-mt'} && $topo->{'cores-st'}){
$rows->[$j]{main::key($num++,1,$id+2,'mt')} = $topo->{'cores-mt'};
$rows->[$j]{main::key($num++,0,$id+3,'tpc')} = $topo->{'tpc'};
@@ -9200,12 +9230,7 @@ sub full_output {
}
$rows->[$j]{main::key($num++,0,$id+1,$key)} = $freq;
}
- if ($topo->{'threads'}){
- $rows->[$j]{main::key($num++,0,$id+1,'threads')} = $topo->{'threads'};
- }
- if ($topo->{'dies'}){
- $rows->[$j]{main::key($num++,0,$id+1,'dies')} = $topo->{'dies'};
- }
+
}
$cpu->{'smt'} ||= 'N/A';
$rows->[$j]{main::key($num++,0,2,'smt')} = $cpu->{'smt'};
@@ -9426,9 +9451,9 @@ sub short_data {
my ($key1,$val1) = ('','');
if ($alerts{'sysctl'}){
if ($alerts{'sysctl'}->{'action'} eq 'use'){
-# $key1 = 'Status';
-# $val1 = main::message('dev');
- $cpu = sysctl_data($type);
+ # $key1 = 'Status';
+ # $val1 = main::message('dev');
+ $cpu = cpu_sysctl_data($type);
}
else {
$key1 = ucfirst($alerts{'sysctl'}->{'action'});
@@ -9472,15 +9497,155 @@ sub prep_short_data {
return $result;
}
-## PRIMARY DATA GENERATORS ##
+## CPUINFO/SYS DATA GENERATORS ##
+
+## DEBUGGER DATA
+# Set in one place to make sure we get them all consistent
+sub set_fake_cpu_data {
+ $loaded{'cpu-fake-data'} = 1;
+ my ($ci,$sys);
+ ## CPUINFO DATA FILES ##
+ ## ARM/MIPS
+ # $ci = "$fake_data_dir/cpu/arm/arm-4-core-pinebook-1.txt";
+ # $ci = "$fake_data_dir/cpu/arm/armv6-single-core-1.txt";
+ # $ci = "$fake_data_dir/cpu/arm/armv7-dual-core-1.txt";
+ # $ci = "$fake_data_dir/cpu/arm/armv7-new-format-model-name-single-core.txt";
+ # $ci = "$fake_data_dir/cpu/arm/arm-2-die-96-core-rk01.txt";
+ # $ci = "$fake_data_dir/cpu/arm/arm-shevaplug-1.2ghz.txt";
+ # $ci = "$fake_data_dir/cpu/mips/mips-mainusg-cpuinfo.txt";
+ # $ci = "$fake_data_dir/cpu/ppc/ppc-debian-ppc64-cpuinfo.txt";
+ ## x86
+ # $ci = "$fake_data_dir/cpu/amd/16-core-32-mt-ryzen.txt";
+ # $ci = "$fake_data_dir/cpu/amd/2-16-core-epyc-abucodonosor.txt";
+ # $ci = "$fake_data_dir/cpu/amd/2-core-probook-antix.txt";
+ # $ci = "$fake_data_dir/cpu/amd/4-core-jean-antix.txt";
+ # $ci = "$fake_data_dir/cpu/amd/4-core-althlon-mjro.txt";
+ # $ci = "$fake_data_dir/cpu/amd/4-core-apu-vc-box.txt";
+ # $ci = "$fake_data_dir/cpu/amd/4-core-a10-5800k-1.txt";
+ # $ci = "$fake_data_dir/cpu/intel/1-core-486-fourtysixandtwo.txt";
+ # $ci = "$fake_data_dir/cpu/intel/2-core-ht-atom-bruh.txt";
+ # $ci = "$fake_data_dir/cpu/intel/core-2-i3.txt";
+ # $ci = "$fake_data_dir/cpu/intel/8-core-i7-damentz64.txt";
+ # $ci = "$fake_data_dir/cpu/intel/2-10-core-xeon-ht.txt";
+ # $ci = "$fake_data_dir/cpu/intel/4-core-xeon-fake-dual-die-zyanya.txt";
+ # $ci = "$fake_data_dir/cpu/intel/2-core-i5-fake-dual-die-hek.txt";
+ # $ci = "$fake_data_dir/cpu/intel/2-1-core-xeon-vm-vs2017.txt";
+ # $ci = "$fake_data_dir/cpu/intel/4-1-core-xeon-vps-frodo1.txt";
+ # $ci = "$fake_data_dir/cpu/intel/4-6-core-xeon-no-mt-lathander.txt";
+ ## Elbrus
+ # $cpu_type = 'elbrus'; # uncomment to test elbrus
+ # $ci = "$fake_data_dir/cpu/elbrus/elbrus-2c3/cpuinfo.txt";
+ # $ci = "$fake_data_dir/cpu/elbrus/1xE1C-8.txt";
+ # $ci = "$fake_data_dir/cpu/elbrus/1xE2CDSP-4.txt";
+ # $ci = "$fake_data_dir/cpu/elbrus/1xE2S4-3-monocub.txt";
+ # $ci = "$fake_data_dir/cpu/elbrus/1xMBE8C-7.txt";
+ # $ci = "$fake_data_dir/cpu/elbrus/4xEL2S4-3.txt";
+ # $ci = "$fake_data_dir/cpu/elbrus/4xE8C-7.txt";
+ # $ci = "$fake_data_dir/cpu/elbrus/4xE2CDSP-4.txt";
+ # $ci = "$fake_data_dir/cpu/elbrus/cpuinfo.e8c2.txt";
+ ## Loongson
+ # $cpu_type = 'elbrus'; # uncomment to test loongson
+ # $ci = "$fake_data_dir/cpu/loongson/3A5000M-4-core-4.19.0.txt";
+
+ ## CPU CPUINFO/SYS PAIRS DATA FILES ##
+ # $ci = "$fake_data_dir/cpu/sys-ci-pairs/android-pocom3-fake-cpuinfo.txt";
+ # $sys = "$fake_data_dir/cpu/sys-ci-pairs/android-pocom3-fake-sys.txt";
+ # $ci = "$fake_data_dir/cpu/sys-ci-pairs/arm-pine64-cpuinfo-1.txt";
+ # $sys = "$fake_data_dir/cpu/sys-ci-pairs/arm-pine64-sys-1.txt";
+ # $ci = "$fake_data_dir/cpu/sys-ci-pairs/arm-riscyslack2-cpuinfo-1.txt";
+ # $sys = "$fake_data_dir/cpu/sys-ci-pairs/arm-riscyslack2-sys-1.txt";
+ # $ci = "$fake_data_dir/cpu/sys-ci-pairs/ppc-stuntkidz~cpuinfo.txt";
+ # $sys = "$fake_data_dir/cpu/sys-ci-pairs/ppc-stuntkidz~sys.txt";
+ # $ci = "$fake_data_dir/cpu/sys-ci-pairs/riscv-unmatched-2021~cpuinfo-1.txt";
+ # $sys = "$fake_data_dir/cpu/sys-ci-pairs/riscv-unmatched-2021~sys-1.txt";
+ # $ci = "$fake_data_dir/cpu/sys-ci-pairs/x86-brickwizard-atom-n270~cpuinfo-1.txt";
+ # $sys = "$fake_data_dir/cpu/sys-ci-pairs/x86-brickwizard-atom-n270~sys-1.txt";
+ # $ci = "$fake_data_dir/cpu/sys-ci-pairs/x86-amd-phenom-chrisretusn-cpuinfo-1.txt";
+ # $sys = "$fake_data_dir/cpu/sys-ci-pairs/x86-amd-phenom-chrisretusn-sys-1.txt";
+ # $ci = "$fake_data_dir/cpu/sys-ci-pairs/x86-drgibbon-intel-i7-cpuinfo.txt";
+ # $sys = "$fake_data_dir/cpu/sys-ci-pairs/x86-drgibbon-intel-i7-sys.txt";
+ # $ci = "$fake_data_dir/cpu/sys-ci-pairs/ryzen-threadripper-1x64-3950x-cpuinfo.txt";
+ # $sys = "$fake_data_dir/cpu/sys-ci-pairs/ryzen-threadripper-1x64-3950x-sys.txt";
+ # $ci = "$fake_data_dir/cpu/sys-ci-pairs/amd-threadripper-1x12-5945wx-cpuinfo-1.txt";
+ # $sys = "$fake_data_dir/cpu/sys-ci-pairs/amd-threadripper-1x12-5945wx-sys-1.txt";
+ # $ci = "$fake_data_dir/cpu/sys-ci-pairs/intel-i7-1165G7-4-core-no-smt-cpuinfo.txt";
+ # $sys = "$fake_data_dir/cpu/sys-ci-pairs/intel-i7-1165G7-4-core-no-smt-sys.txt";
+ # $ci = "$fake_data_dir/cpu/sys-ci-pairs/elbrus-e16c-1-cpuinfo.txt";
+ # $sys = "$fake_data_dir/cpu/sys-ci-pairs/elbrus-e16c-1-sys.txt";
+ # $ci = "$fake_data_dir/cpu/sys-ci-pairs/intel-raptor-lake-10-core-cpuinfo-1.txt";
+ # $sys = "$fake_data_dir/cpu/sys-ci-pairs/intel-raptor-lake-10-core-sys-1.txt";
+ # $ci = "$fake_data_dir/cpu/sys-ci-pairs/risc-v-spacemit-8-core-cpuinfo.txt";
+ # $sys = "$fake_data_dir/cpu/sys-ci-pairs/risc-v-spacemit-8-core-sys.txt";
+ # $ci = "$fake_data_dir/cpu/sys-ci-pairs/intel-xeon-2x12-core-mt-cpuinfo-1.txt";
+ # $sys = "$fake_data_dir/cpu/sys-ci-pairs/intel-xeon-2x12-core-mt-sys-1.txt";
+ $ci = "$fake_data_dir/cpu/sys-ci-pairs/amd-epyc-2x-16-core-4-die-cpuinfo-1.txt";
+ $sys = "$fake_data_dir/cpu/sys-ci-pairs/amd-epyc-2x-16-core-4-die-sys-1.txt";
+ $fake_data{'cpuinfo'} = $ci;
+ $fake_data{'sys'} = $sys;
+}
+
+## CPUINFO DATA
+sub cpuinfo_data_grabber {
+ eval $start if $b_log;
+ my ($file,$cpu_type) = @_; # type by ref
+ $loaded{'cpuinfo'} = 1;
+ # use --arm flag when testing arm cpus, and --fake-cpu to trigger fake data
+ $file = $fake_data{'cpuinfo'} if $fake{'cpu'};
+ my $raw = main::reader($file,'','ref');
+ @$raw = map {$_ =~ s/^\s*$/~~~/;$_;} @$raw;
+ push(@$raw,'~~~') if @$raw;
+ my ($b_processor,$key,$value);
+ my ($i) = (0);
+ my @key_tests = ('firmware','hardware','mmu','model','motherboard',
+ 'platform','system type','timebase');
+ foreach my $row (@$raw){
+ ($key,$value) = split(/\s*:\s*/,$row,2);
+ next if !defined $key;
+ # ARM: 'Hardware' can appear in processor block; system type (mips)
+ # ARM: CPU revision; machine: Revision/PPC: revision (CPU implied)
+ # orangepi3 has Hardware/Processor embedded in processor block
+ if (%risc && ((grep {lc($key) eq $_} @key_tests) ||
+ (!$risc{'ppc'} && lc($key) eq 'revision'))){
+ $b_processor = 0;
+ }
+ else {
+ $b_processor = 1;
+ }
+ if ($b_processor){
+ if ($key eq '~~~'){
+ $i++;
+ next;
+ }
+ # A small handful of ARM devices use Processor instead of 'model name'
+ # Processor : AArch64 Processor rev 4 (aarch64)
+ # Processor : Feroceon 88FR131 rev 1 (v5l)
+ $key = ($key eq 'Processor') ? 'model name' : lc($key);
+ $cpuinfo[$i]->{$key} = $value;
+ }
+ else {
+ next if $cpuinfo_machine{lc($key)};
+ $cpuinfo_machine{lc($key)} = $value;
+ }
+ }
+ if ($b_log){
+ main::log_data('dump','@cpuinfo',\@cpuinfo);
+ main::log_data('dump','%cpuinfo_machine',\%cpuinfo_machine);
+ }
+ if ($dbg[41]){
+ print Data::Dumper::Dumper \@cpuinfo;
+ print Data::Dumper::Dumper \%cpuinfo_machine;
+ }
+ eval $end if $b_log;
+}
+
sub cpuinfo_data {
eval $start if $b_log;
my ($file)= @_;
my ($cpu,$arch,$note,$temp);
# has to be set above fake cpu section
set_cpu_data(\$cpu);
- set_fake_data() if $fake{'cpu'} && !$loaded{'cpu-fake-data'};
- # sleep is also set in front of sysctl_data for BSDs, same idea
+ set_fake_cpu_data() if $fake{'cpu'} && !$loaded{'cpu-fake-data'};
+ # sleep is also set in front of cpu_sysctl_data for BSDs, same idea
my $sleep = $cpu_sleep * 1000000;
if ($b_hires){
eval 'Time::HiRes::usleep($sleep)';
@@ -9772,159 +9937,277 @@ sub cpuinfo_speed_sys {
eval $end if $b_log;
}
-sub cpuinfo_data_grabber {
+## SYS DATA
+sub cpu_sys_data_grabber {
eval $start if $b_log;
- my ($file,$cpu_type) = @_; # type by ref
- $loaded{'cpuinfo'} = 1;
- # use --arm flag when testing arm cpus, and --fake-cpu to trigger fake data
- $file = $fake_data{'cpuinfo'} if $fake{'cpu'};
- my $raw = main::reader($file,'','ref');
- @$raw = map {$_ =~ s/^\s*$/~~~/;$_;} @$raw;
- push(@$raw,'~~~') if @$raw;
- my ($b_processor,$key,$value);
- my ($i) = (0);
- my @key_tests = ('firmware','hardware','mmu','model','motherboard',
- 'platform','system type','timebase');
- foreach my $row (@$raw){
- ($key,$value) = split(/\s*:\s*/,$row,2);
- next if !defined $key;
- # ARM: 'Hardware' can appear in processor block; system type (mips)
- # ARM: CPU revision; machine: Revision/PPC: revision (CPU implied)
- # orangepi3 has Hardware/Processor embedded in processor block
- if (%risc && ((grep {lc($key) eq $_} @key_tests) ||
- (!$risc{'ppc'} && lc($key) eq 'revision'))){
- $b_processor = 0;
+ my (@files);
+ set_fake_cpu_data() if $fake{'cpu'} && !$loaded{'cpu-fake-data'};
+ # this data has to match the data in cpuinfo grabber fake cpu, and remember
+ # to use --arm flag if arm tests
+ if ($fake{'cpu'}){
+ # print "$fake_data{'sys'}\n";
+ @files = main::reader($fake_data{'sys'}) if $fake_data{'sys'};
+ # print Data::Dumper::Dumper \@files;
+ }
+ # There's a massive time hit reading full globbed set of files, so grab and
+ # read only what we need.
+ else {
+ my $glob = '/sys/devices/system/cpu/{';
+ if ($dbg[43]){
+ $glob .= 'cpufreq,cpu*/topology,cpu*/cpufreq,cpu*/cache/index*,smt,vulnerabilities}/*';
}
else {
- $b_processor = 1;
+ $glob .= 'cpu*/topology/';
+ $glob .= '{{cluster_cpus,core_cpus,core_siblings,thread_siblings}_list,';
+ $glob .= '{core,die,cluster,node,physical_package,socket}_id}';
+ $glob .= ',cpufreq/{boost,ondemand}';
+ $glob .= ',cpu*/cpufreq/';
+ $glob .= '{cpb,{cpuinfo_max,cpuinfo_min,scaling_max,scaling_min}_freq';
+ if ($type eq 'full' && $b_admin){
+ $glob .= ',scaling_driver,scaling_governor';
+ }
+ $glob .= '}';
+ if ($type eq 'full'){
+ $glob .= ',cpu*/cache/index*/{level,shared_cpu_list,shared_cpu_map,size,type}';
+ }
+ $glob .= ',smt/{active,control}';
+ $glob .= ',vulnerabilities/*' if $b_admin;
+ $glob .= '}';
}
- if ($b_processor){
- if ($key eq '~~~'){
- $i++;
- next;
+ # print "sys glob: $glob\n";
+ @files = main::globber($glob);
+ }
+ main::log_data('dump','@files',\@files) if $b_log;
+ print Data::Dumper::Dumper \@files if $dbg[40];
+ my ($b_bug,$b_cache,$b_freq,$b_topo,$b_main);
+ my $working = {};
+ my ($main_id,$main_key,$holder,$id,$item,$key) = ('','','','','','');
+ # need to return hash reference on failure or old systems complain
+ return $working if !@files;
+ foreach (sort @files){
+ if ($fake{'cpu'}){
+ ($_,$item) = split(/::/,$_,2);
+ }
+ else {
+ next if -d $_ || ! -e $_;
+ undef $item;
+ }
+ $key = $_;
+ $key =~ m|/([^/]+)/([^/]+)$|;
+ my ($key_1,$key_2) = ($1,$2);
+ if (m|/cpu(\d+)/|){
+ if (!$holder || $1 ne $holder){
+ $id = sprintf("%04d",$1);
+ $holder = $1;
}
- # A small handful of ARM devices use Processor instead of 'model name'
- # Processor : AArch64 Processor rev 4 (aarch64)
- # Processor : Feroceon 88FR131 rev 1 (v5l)
- $key = ($key eq 'Processor') ? 'model name' : lc($key);
- $cpuinfo[$i]->{$key} = $value;
+ $b_bug = 0;
+ $b_cache = 0;
+ $b_freq = 0;
+ $b_main = 0;
+ $b_topo = 0;
+ if ($key_1 eq 'cpufreq'){
+ $b_freq = 1;
+ $main_key = $key_2;
+ $key = $key_1;
+ }
+ elsif ($key_1 eq 'topology'){
+ $b_topo = 1;
+ $main_key = $key_2;
+ $key = $key_1;
+ }
+ elsif ($key_1 =~ /^index(\d+)$/){
+ $b_cache = 1;
+ $main_key = $key_2;
+ $main_id = sprintf("%02d",$1);
+ $key = 'cache';
+ }
+ }
+ elsif ($key_1 eq 'vulnerabilities'){
+ $id = $key_1;
+ $key = $key_2;
+ $b_bug = 1;
+ $b_cache = 0;
+ $b_main = 0;
+ $b_freq = 0;
+ $b_topo = 0;
+ $main_key = '';
+ $main_id = '';
}
else {
- next if $cpuinfo_machine{lc($key)};
- $cpuinfo_machine{lc($key)} = $value;
+ $id = $key_1 . '-' . $key_2;
+ $b_bug = 0;
+ $b_cache = 0;
+ $b_main = 1;
+ $b_freq = 0;
+ $b_topo = 0;
+ $main_key = '';
+ $main_id = '';
+ }
+ if (!$fake{'cpu'}){
+ if (-r $_) {
+ my $error;
+ # significantly faster to skip reader() and do it directly
+ # $fh always non null, even on error
+ open(my $fh, '<', $_) or $error = $!;
+ if (!$error){
+ chomp(my @value = <$fh>);
+ close $fh;
+ $item = $value[0];
+ }
+ # $item = main::reader($_,'strip',0);
+ }
+ else {
+ $item = main::message('root-required');
+ }
+ $item = main::message('undefined') if !defined $item;
+ }
+ # print "$key_1 :: $key_2 :: $item\n";
+ if ($b_main){
+ $working->{'data'}{$id} = $item;
+ }
+ elsif ($b_bug){
+ my $type = ($item =~ /^Mitigation:/) ? 'mitigation': 'status';
+ $item =~ s/Mitigation: //;
+ $working->{'data'}{$id}{$key} = [$type,$item];
+ }
+ elsif ($b_cache){
+ $working->{'cpus'}{$id}{$key}{$main_id}{$main_key} = $item;
+ }
+ elsif ($b_freq || $b_topo){
+ $working->{'cpus'}{$id}{$key}{$main_key} = $item;
}
}
- if ($b_log){
- main::log_data('dump','@cpuinfo',\@cpuinfo);
- main::log_data('dump','%cpuinfo_machine',\%cpuinfo_machine);
- }
- if ($dbg[41]){
- print Data::Dumper::Dumper \@cpuinfo;
- print Data::Dumper::Dumper \%cpuinfo_machine;
- }
+ main::log_data('dump','%$working',$working) if $b_log;
+ print Data::Dumper::Dumper $working if $dbg[39];
eval $end if $b_log;
+ return $working;
}
sub cpu_sys_data {
eval $start if $b_log;
my $sys_freq = $_[0];
my $cpu_sys = {};
- my $working = sys_data_grabber();
+ my $working = cpu_sys_data_grabber();
return $cpu_sys if !%$working;
$cpu_sys->{'data'} = $working->{'data'} if $working->{'data'};
- my ($core_id,$fake_core_id,$phys_id,) = (0,0,-1);
+ my ($core_id,$fake_core_id,$die_id,$phys_id) = (0,0,0,-1);
my (%cache_ids,@ci_freq_max,@ci_freq_min,@sc_freq_max,@sc_freq_min);
- foreach my $key (sort keys %{$working->{'cpus'}}){
+ foreach my $id (sort keys %{$working->{'cpus'}}){
($core_id,$phys_id) = (0,0);
- my $cpu_id = $key + 0;
- my $speed;
- my $cpu = $working->{'cpus'}{$key};
- if (defined $cpu->{'topology'}{'physical_package_id'}){
- $phys_id = sprintf("%04d",$cpu->{'topology'}{'physical_package_id'});
+ my $cpu_id = $id + 0;
+ my ($cluster_id,$speed);
+ my $phys_cpu = $working->{'cpus'}{$id};
+ if (defined $phys_cpu->{'topology'}{'physical_package_id'}){
+ $phys_id = sprintf("%04d",$phys_cpu->{'topology'}{'physical_package_id'});
}
- if (defined $cpu->{'topology'}{'core_id'}){
+ if (defined $phys_cpu->{'topology'}{'die_id'}){
+ $cpu_sys->{'data'}{'die-file'} = 'die_id';
+ $die_id = sprintf("%08d",$phys_cpu->{'topology'}{$cpu_sys->{'data'}{'die-file'}});
+ }
+ else {
+ $die_id = 'ID-UNSET';
+ }
+ # RISCV seen with no die_id but cluster_id with core_ids per cluster
+ # node_id, socket_id not seen but possibles but don't use until real case
+ # also alder lake haw one die but > 1 clusters
+ if (defined $phys_cpu->{'topology'}{'cluster_id'}){
+ $cpu_sys->{'data'}{'cluster-file'} = 'cluster_id';
+ $cluster_id = sprintf("%08d",$phys_cpu->{'topology'}{$cpu_sys->{'data'}{'cluster-file'}});
+ }
+ if (defined $phys_cpu->{'topology'}{'core_id'}){
# id is not consistent, seen 5 digit id
- $core_id = sprintf("%08d",$cpu->{'topology'}{'core_id'});
+ $core_id = sprintf("%08d",$phys_cpu->{'topology'}{'core_id'});
if ($fake{'cpu'}){
- if (defined $cpu->{'cpufreq'}{'scaling_cur_freq'} &&
- $cpu->{'cpufreq'}{'affected_cpus'} &&
- $cpu->{'cpufreq'}{'affected_cpus'} ne 'UNDEFINED' &&
+ if (defined $phys_cpu->{'cpufreq'}{'scaling_cur_freq'} &&
+ $phys_cpu->{'cpufreq'}{'affected_cpus'} &&
+ $phys_cpu->{'cpufreq'}{'affected_cpus'} ne 'UNDEFINED' &&
# manually generated cpu debuggers will show '', not UNDEFINED
- $cpu->{'cpufreq'}{'affected_cpus'} ne ''){
- $speed = clean_speed($cpu->{'cpufreq'}{'scaling_cur_freq'},'khz');
+ $phys_cpu->{'cpufreq'}{'affected_cpus'} ne ''){
+ $speed = clean_speed($phys_cpu->{'cpufreq'}{'scaling_cur_freq'},'khz');
}
}
- elsif (defined $sys_freq && defined $sys_freq->{$key}){
- $speed = $sys_freq->{$key};
+ elsif (defined $sys_freq && defined $sys_freq->{$phys_id}){
+ $speed = $sys_freq->{$phys_id};
}
+ # ($cluster_id,$die_id) = ();
if (defined $speed){
- push(@{$cpu_sys->{'cpus'}{$phys_id}{'cores'}{$core_id}},$speed);
+ if ($cpu_sys->{'data'}{'die-file'} || !$cluster_id){
+ push(@{$cpu_sys->{'cpus'}{$phys_id}{'dies'}{$die_id}{'cores'}{$core_id}},$speed);
+ }
+ if ($cluster_id){
+ push(@{$cpu_sys->{'cpus'}{$phys_id}{'dies'}{$die_id}{'clusters'}{$cluster_id}{'cores'}{$core_id}},$speed);
+ }
push(@{$cpu_sys->{'data'}{'speeds'}{'all'}},$speed);
}
else {
push(@{$cpu_sys->{'data'}{'speeds'}{'all'}},0);
# seen cases, riscv, where core id, phys id, are all -1
my $id = ($core_id != -1) ? $core_id: $fake_core_id++;
- push(@{$cpu_sys->{'cpus'}{$phys_id}{'cores'}{$id}},0);
+ if ($cpu_sys->{'data'}{'die-file'} || !$cluster_id){
+ push(@{$cpu_sys->{'cpus'}{$phys_id}{'dies'}{$die_id}{'cores'}{$id}},0);
+ }
+ if ($cluster_id){
+ push(@{$cpu_sys->{'cpus'}{$phys_id}{'dies'}{$die_id}{'clusters'}{$cluster_id}{'cores'}{$id}},0);
+ }
}
# Only use if topology core-id exists, some virtualized cpus can list
# frequency data for the non available cores, but those do not show
# topology data.
# For max / min, we want to prep for the day 1 pys cpu has > 1 min/max freq
- if (defined $cpu->{'cpufreq'}{'cpuinfo_max_freq'}){
- $cpu->{'cpufreq'}{'cpuinfo_max_freq'} = clean_speed($cpu->{'cpufreq'}{'cpuinfo_max_freq'},'khz');
- if (!grep {$_ eq $cpu->{'cpufreq'}{'cpuinfo_max_freq'}} @ci_freq_max){
- push(@ci_freq_max,$cpu->{'cpufreq'}{'cpuinfo_max_freq'});
+ if (defined $phys_cpu->{'cpufreq'}{'cpuinfo_max_freq'}){
+ $phys_cpu->{'cpufreq'}{'cpuinfo_max_freq'} = clean_speed($phys_cpu->{'cpufreq'}{'cpuinfo_max_freq'},'khz');
+ if (!grep {$_ eq $phys_cpu->{'cpufreq'}{'cpuinfo_max_freq'}} @ci_freq_max){
+ push(@ci_freq_max,$phys_cpu->{'cpufreq'}{'cpuinfo_max_freq'});
}
- if (!grep {$_ eq $cpu->{'cpufreq'}{'cpuinfo_max_freq'}} @{$cpu_sys->{'cpus'}{$phys_id}{'max-freq'}}){
- push(@{$cpu_sys->{'cpus'}{$phys_id}{'max-freq'}},$cpu->{'cpufreq'}{'cpuinfo_max_freq'});
+ if (!grep {$_ eq $phys_cpu->{'cpufreq'}{'cpuinfo_max_freq'}} @{$cpu_sys->{'cpus'}{$phys_id}{'max-freq'}}){
+ push(@{$cpu_sys->{'cpus'}{$phys_id}{'max-freq'}},$phys_cpu->{'cpufreq'}{'cpuinfo_max_freq'});
}
}
- if (defined $cpu->{'cpufreq'}{'cpuinfo_min_freq'}){
- $cpu->{'cpufreq'}{'cpuinfo_min_freq'} = clean_speed($cpu->{'cpufreq'}{'cpuinfo_min_freq'},'khz');
- if (!grep {$_ eq $cpu->{'cpufreq'}{'cpuinfo_min_freq'}} @ci_freq_min){
- push(@ci_freq_min,$cpu->{'cpufreq'}{'cpuinfo_min_freq'});
+ if (defined $phys_cpu->{'cpufreq'}{'cpuinfo_min_freq'}){
+ $phys_cpu->{'cpufreq'}{'cpuinfo_min_freq'} = clean_speed($phys_cpu->{'cpufreq'}{'cpuinfo_min_freq'},'khz');
+ if (!grep {$_ eq $phys_cpu->{'cpufreq'}{'cpuinfo_min_freq'}} @ci_freq_min){
+ push(@ci_freq_min,$phys_cpu->{'cpufreq'}{'cpuinfo_min_freq'});
}
- if (!grep {$_ eq $cpu->{'cpufreq'}{'cpuinfo_min_freq'}} @{$cpu_sys->{'cpus'}{$phys_id}{'min-freq'}}){
- push(@{$cpu_sys->{'cpus'}{$phys_id}{'min-freq'}},$cpu->{'cpufreq'}{'cpuinfo_min_freq'});
+ if (!grep {$_ eq $phys_cpu->{'cpufreq'}{'cpuinfo_min_freq'}} @{$cpu_sys->{'cpus'}{$phys_id}{'min-freq'}}){
+ push(@{$cpu_sys->{'cpus'}{$phys_id}{'min-freq'}},$phys_cpu->{'cpufreq'}{'cpuinfo_min_freq'});
}
}
- if (defined $cpu->{'cpufreq'}{'scaling_max_freq'}){
- $cpu->{'cpufreq'}{'scaling_max_freq'} = clean_speed($cpu->{'cpufreq'}{'scaling_max_freq'},'khz');
- if (!grep {$_ eq $cpu->{'cpufreq'}{'scaling_max_freq'}} @sc_freq_max){
- push(@sc_freq_max,$cpu->{'cpufreq'}{'scaling_max_freq'});
+ if (defined $phys_cpu->{'cpufreq'}{'scaling_max_freq'}){
+ $phys_cpu->{'cpufreq'}{'scaling_max_freq'} = clean_speed($phys_cpu->{'cpufreq'}{'scaling_max_freq'},'khz');
+ if (!grep {$_ eq $phys_cpu->{'cpufreq'}{'scaling_max_freq'}} @sc_freq_max){
+ push(@sc_freq_max,$phys_cpu->{'cpufreq'}{'scaling_max_freq'});
}
- if (!grep {$_ eq $cpu->{'cpufreq'}{'scaling_max_freq'}} @{$cpu_sys->{'cpus'}{$phys_id}{'max-freq'}}){
- push(@{$cpu_sys->{'cpus'}{$phys_id}{'max-freq'}},$cpu->{'cpufreq'}{'scaling_max_freq'});
+ if (!grep {$_ eq $phys_cpu->{'cpufreq'}{'scaling_max_freq'}} @{$cpu_sys->{'cpus'}{$phys_id}{'max-freq'}}){
+ push(@{$cpu_sys->{'cpus'}{$phys_id}{'max-freq'}},$phys_cpu->{'cpufreq'}{'scaling_max_freq'});
}
}
- if (defined $cpu->{'cpufreq'}{'scaling_min_freq'}){
- $cpu->{'cpufreq'}{'scaling_min_freq'} = clean_speed($cpu->{'cpufreq'}{'scaling_min_freq'},'khz');
- if (!grep {$_ eq $cpu->{'cpufreq'}{'scaling_min_freq'}} @sc_freq_min){
- push(@sc_freq_min,$cpu->{'cpufreq'}{'scaling_min_freq'});
+ if (defined $phys_cpu->{'cpufreq'}{'scaling_min_freq'}){
+ $phys_cpu->{'cpufreq'}{'scaling_min_freq'} = clean_speed($phys_cpu->{'cpufreq'}{'scaling_min_freq'},'khz');
+ if (!grep {$_ eq $phys_cpu->{'cpufreq'}{'scaling_min_freq'}} @sc_freq_min){
+ push(@sc_freq_min,$phys_cpu->{'cpufreq'}{'scaling_min_freq'});
}
- if (!grep {$_ eq $cpu->{'cpufreq'}{'scaling_min_freq'}} @{$cpu_sys->{'cpus'}{$phys_id}{'min-freq'}}){
- push(@{$cpu_sys->{'cpus'}{$phys_id}{'min-freq'}},$cpu->{'cpufreq'}{'scaling_min_freq'});
+ if (!grep {$_ eq $phys_cpu->{'cpufreq'}{'scaling_min_freq'}} @{$cpu_sys->{'cpus'}{$phys_id}{'min-freq'}}){
+ push(@{$cpu_sys->{'cpus'}{$phys_id}{'min-freq'}},$phys_cpu->{'cpufreq'}{'scaling_min_freq'});
}
}
- if (defined $cpu->{'cpufreq'}{'scaling_governor'}){
- if (!grep {$_ eq $cpu->{'cpufreq'}{'scaling_governor'}} @{$cpu_sys->{'cpus'}{$phys_id}{'governor'}}){
- push(@{$cpu_sys->{'cpus'}{$phys_id}{'governor'}},$cpu->{'cpufreq'}{'scaling_governor'});
+ if (defined $phys_cpu->{'cpufreq'}{'scaling_governor'}){
+ if (!grep {$_ eq $phys_cpu->{'cpufreq'}{'scaling_governor'}} @{$cpu_sys->{'cpus'}{$phys_id}{'governor'}}){
+ push(@{$cpu_sys->{'cpus'}{$phys_id}{'governor'}},$phys_cpu->{'cpufreq'}{'scaling_governor'});
}
}
- if (defined $cpu->{'cpufreq'}{'scaling_driver'}){
- $cpu_sys->{'cpus'}{$phys_id}{'scaling-driver'} = $cpu->{'cpufreq'}{'scaling_driver'};
+ if (defined $phys_cpu->{'cpufreq'}{'scaling_driver'}){
+ $cpu_sys->{'cpus'}{$phys_id}{'scaling-driver'} = $phys_cpu->{'cpufreq'}{'scaling_driver'};
}
}
- if (!defined $cpu_sys->{'data'}{'cpufreq-boost'} && defined $cpu->{'cpufreq'}{'cpb'}){
- $cpu_sys->{'data'}{'cpufreq-boost'} = $cpu->{'cpufreq'}{'cpb'};
+ if (!defined $cpu_sys->{'data'}{'cpufreq-boost'} && defined $phys_cpu->{'cpufreq'}{'cpb'}){
+ $cpu_sys->{'data'}{'cpufreq-boost'} = $phys_cpu->{'cpufreq'}{'cpb'};
}
- if (defined $cpu->{'topology'}{'core_cpus_list'}){
- $cpu->{'topology'}{'thread_siblings_list'} = $cpu->{'topology'}{'core_cpus_list'};
+ if (defined $phys_cpu->{'topology'}{'core_cpus_list'}){
+ $phys_cpu->{'topology'}{'thread_siblings_list'} = $phys_cpu->{'topology'}{'core_cpus_list'};
}
- if (defined $cpu->{'cache'} && keys %{$cpu->{'cache'}} > 0){
- foreach my $key2 (sort keys %{$cpu->{'cache'}}){
- my $cache = $cpu->{'cache'}{$key2};
+ if (defined $phys_cpu->{'cache'} && keys %{$phys_cpu->{'cache'}} > 0){
+ foreach my $key2 (sort keys %{$phys_cpu->{'cache'}}){
+ my $cache = $phys_cpu->{'cache'}{$key2};
my $type = ($cache->{'type'} =~ /^([DI])/i) ? lc($1): '';
my $level = 'l' . $cache->{'level'} . $type;
# Very old systems, 2.6.xx do not have shared_cpu_list
@@ -9944,15 +10227,6 @@ sub cpu_sys_data {
}
}
}
- # die_id is relatively new, core_siblings_list has been around longer
- if (defined $cpu->{'topology'}{'die_id'} ||
- defined $cpu->{'topology'}{'core_siblings_list'}){
- my $die = $cpu->{'topology'}{'die_id'};
- $die = $cpu->{'topology'}{'core_siblings_list'} if !defined $die;
- if (!grep {$_ eq $die} @{$cpu_sys->{'cpus'}{$phys_id}{'dies'}}){
- push(@{$cpu_sys->{'cpus'}{$phys_id}{'dies'}},$die);
- }
- }
}
if (defined $cpu_sys->{'data'}{'cpufreq-boost'} &&
$cpu_sys->{'data'}{'cpufreq-boost'} =~ /^[01]$/){
@@ -9993,230 +10267,225 @@ sub cpu_sys_data {
$cpu_sys->{'data'}{'speeds'}{'min-freq'} = 0;
}
main::log_data('dump','%$cpu_sys',$cpu_sys) if $b_log;
- print 'cpu-sys: ', Data::Dumper::Dumper $cpu_sys if $dbg[8];
+ print 'cpu_sys_data: %$cpu-sys: ', Data::Dumper::Dumper $cpu_sys if $dbg[8];
eval $end if $b_log;
return $cpu_sys;
}
-sub sys_data_grabber {
+# all values passed by reference so no need for returns
+sub cp_data_sys {
eval $start if $b_log;
- my (@files);
- set_fake_data() if $fake{'cpu'} && !$loaded{'cpu-fake-data'};
- # this data has to match the data in cpuinfo grabber fake cpu, and remember
- # to use --arm flag if arm tests
- if ($fake{'cpu'}){
- # print "$fake_data{'sys'}\n";
- @files = main::reader($fake_data{'sys'}) if $fake_data{'sys'};
- # print Data::Dumper::Dumper \@files;
+ my ($cpu,$cpu_sys,$caches,$counts) = @_;
+ my @phys_keys = sort keys %{$cpu_sys->{'cpus'}};
+ return if ! @phys_keys;
+ $counts->{'physical'} = scalar @phys_keys;
+ if ($type eq 'full' && $cpu_sys->{'cpus'}{$phys_keys[0]}{'caches'}){
+ cp_sys_caches($cpu_sys->{'cpus'}{$phys_keys[0]}{'caches'},$caches,'l1','l1d');
+ cp_sys_caches($cpu_sys->{'cpus'}{$phys_keys[0]}{'caches'},$caches,'l1','l1i');
+ cp_sys_caches($cpu_sys->{'cpus'}{$phys_keys[0]}{'caches'},$caches,'l2','');
+ cp_sys_caches($cpu_sys->{'cpus'}{$phys_keys[0]}{'caches'},$caches,'l3','');
}
- # There's a massive time hit reading full globbed set of files, so grab and
- # read only what we need.
- else {
- my $glob = '/sys/devices/system/cpu/{';
- if ($dbg[43]){
- $glob .= 'cpufreq,cpu*/topology,cpu*/cpufreq,cpu*/cache/index*,smt,vulnerabilities}/*';
- }
- else {
- $glob .= 'cpu*/topology/{core_cpus_list,core_id,core_siblings_list,die_id,';
- $glob .= 'physical_package_id,thread_siblings_list}';
- $glob .= ',cpufreq/{boost,ondemand}';
- $glob .= ',cpu*/cpufreq/{cpb,cpuinfo_max_freq,cpuinfo_min_freq,';
- $glob .= 'scaling_max_freq,scaling_min_freq';
- $glob .= ',scaling_driver,scaling_governor' if $type eq 'full' && $b_admin;
- $glob .= '}';
- if ($type eq 'full'){
- $glob .= ',cpu*/cache/index*/{level,shared_cpu_list,shared_cpu_map,size,type}';
- }
- $glob .= ',smt/{active,control}';
- $glob .= ',vulnerabilities/*' if $b_admin;
- $glob .= '}';
- }
- # print "sys glob: $glob\n";
- @files = main::globber($glob);
+ if ($cpu_sys->{'data'}{'speeds'}{'all'}){
+ $counts->{'processors'} = scalar @{$cpu_sys->{'data'}{'speeds'}{'all'}};
}
- main::log_data('dump','@files',\@files) if $b_log;
- print Data::Dumper::Dumper \@files if $dbg[40];
- my ($b_bug,$b_cache,$b_freq,$b_topo,$b_main);
- my $working = {};
- my ($main_id,$main_key,$holder,$id,$item,$key) = ('','','','','','');
- # need to return hash reference on failure or old systems complain
- return $working if !@files;
- foreach (sort @files){
- if ($fake{'cpu'}){
- ($_,$item) = split(/::/,$_,2);
+ if (defined $cpu_sys->{'data'}{'smt-active'}){
+ if ($cpu_sys->{'data'}{'smt-active'}){
+ $cpu->{'smt'} = 'enabled';
+ }
+ # values: on/off/notsupported/notimplemented
+ elsif (defined $cpu_sys->{'data'}{'smt-control'} &&
+ $cpu_sys->{'data'}{'smt-control'} =~ /^not/){
+ $cpu->{'smt'} = main::message('unsupported');
}
else {
- next if -d $_ || ! -e $_;
- undef $item;
+ $cpu->{'smt'} = 'disabled';
}
- $key = $_;
- $key =~ m|/([^/]+)/([^/]+)$|;
- my ($key_1,$key_2) = ($1,$2);
- if (m|/cpu(\d+)/|){
- if (!$holder || $1 ne $holder){
- $id = sprintf("%04d",$1);
- $holder = $1;
+ }
+ my $i = 0;
+ my ($b_skip,@governor,@max,@min,@phys_cores);
+ foreach my $phys_id (@phys_keys){
+ cp_dies_clusters(
+ $cpu,
+ $counts,
+ $cpu_sys->{'cpus'}{$phys_id},
+ $cpu_sys->{'data'},
+ $i,
+ $b_skip);
+ $b_skip = 1; # skips count->{cpu-cores} after first phys iteration
+ foreach my $die_id (sort keys %{$cpu_sys->{'cpus'}{$phys_id}{'dies'}}){
+ # If we ever get > 1 min/max speed per phy cpu, we'll need to fix the [0]
+ if ($cpu_sys->{'cpus'}{$phys_id}{'max-freq'}[0]){
+ if (!grep {$cpu_sys->{'cpus'}{$phys_id}{'max-freq'}[0] eq $_} @max){
+ push(@max,$cpu_sys->{'cpus'}{$phys_id}{'max-freq'}[0]);
+ }
+ $counts->{'cpu-topo'}[$i]{'max'} = $cpu_sys->{'cpus'}{$phys_id}{'max-freq'}[0];
}
- $b_bug = 0;
- $b_cache = 0;
- $b_freq = 0;
- $b_main = 0;
- $b_topo = 0;
- if ($key_1 eq 'cpufreq'){
- $b_freq = 1;
- $main_key = $key_2;
- $key = $key_1;
+ if ($cpu_sys->{'cpus'}{$phys_id}{'min-freq'}[0]){
+ if (!grep {$cpu_sys->{'cpus'}{$phys_id}{'min-freq'}[0] eq $_} @min){
+ push(@min,$cpu_sys->{'cpus'}{$phys_id}{'min-freq'}[0]);
+ }
+ $counts->{'cpu-topo'}[$i]{'min'} = $cpu_sys->{'cpus'}{$phys_id}{'min-freq'}[0];
}
- elsif ($key_1 eq 'topology'){
- $b_topo = 1;
- $main_key = $key_2;
- $key = $key_1;
+ # cheating, this is not a count, but we need the data for topology, must
+ # sort since governors can be in different order if > 1
+ if ($cpu_sys->{'cpus'}{$phys_id}{'governor'}){
+ foreach my $gov (@{$cpu_sys->{'cpus'}{$phys_id}{'governor'}}){
+ push(@governor,$gov) if !grep {$_ eq $gov} @governor;
+ }
+ $cpu->{'governor'} = join(',',@governor);
}
- elsif ($key_1 =~ /^index(\d+)$/){
- $b_cache = 1;
- $main_key = $key_2;
- $main_id = sprintf("%02d",$1);
- $key = 'cache';
+ if ($cpu_sys->{'cpus'}{$phys_id}{'scaling-driver'}){
+ $cpu->{'scaling-driver'} = $cpu_sys->{'cpus'}{$phys_id}{'scaling-driver'};
+ }
+ if ($cpu_sys->{'cpus'}{$phys_id}{'scaling-driver'}){
+ $cpu->{'scaling-driver'} = $cpu_sys->{'cpus'}{$phys_id}{'scaling-driver'};
+ }
+ if ($cpu_sys->{'cpus'}{$phys_id}{'scaling-max-freq'}){
+ $cpu->{'scaling-max-freq'} = $cpu_sys->{'cpus'}{$phys_id}{'scaling-max-freq'};
+ }
+ if ($cpu_sys->{'cpus'}{$phys_id}{'scaling-min-freq'}){
+ $cpu->{'scaling-min-freq'} = $cpu_sys->{'cpus'}{$phys_id}{'scaling-min-freq'};
+ }
+ if (!grep {$counts->{'cpu-cores'} eq $_} @phys_cores){
+ push(@phys_cores,$counts->{'cpu-cores'});
}
}
- elsif ($key_1 eq 'vulnerabilities'){
- $id = $key_1;
- $key = $key_2;
- $b_bug = 1;
- $b_cache = 0;
- $b_main = 0;
- $b_freq = 0;
- $b_topo = 0;
- $main_key = '';
- $main_id = '';
- }
- else {
- $id = $key_1 . '-' . $key_2;
- $b_bug = 0;
- $b_cache = 0;
- $b_main = 1;
- $b_freq = 0;
- $b_topo = 0;
- $main_key = '';
- $main_id = '';
- }
- if (!$fake{'cpu'}){
- if (-r $_) {
- my $error;
- # significantly faster to skip reader() and do it directly
- # $fh always non null, even on error
- open(my $fh, '<', $_) or $error = $!;
- if (!$error){
- chomp(my @value = <$fh>);
- close $fh;
- $item = $value[0];
+ if ($counts->{'processors'} && $counts->{'processors'} > $counts->{'cpu-cores'}){
+ foreach my $die_id (sort keys %{$cpu_sys->{'cpus'}{$phys_id}{'dies'}}){
+ if (!$cpu_sys->{'cpus'}{$phys_id}{'dies'}{$die_id}{'clusters'}){
+ if ($cpu_sys->{'cpus'}{$phys_id}{'dies'}{$die_id}{'cores'}){
+ cp_set_threads(
+ $cpu_sys->{'cpus'}{$phys_id}{'dies'}{$die_id}{'cores'},
+ $counts,
+ $i);
+ }
}
- # $item = main::reader($_,'strip',0);
- }
- else {
- $item = main::message('root-required');
+ else {
+ foreach my $cluster_id (sort keys %{$cpu_sys->{'cpus'}{$phys_id}{'dies'}{$die_id}{'clusters'}}){
+ if ($cpu_sys->{'cpus'}{$phys_id}{'dies'}{$die_id}{'clusters'}{$cluster_id}{'cores'}){
+ cp_set_threads(
+ $cpu_sys->{'cpus'}{$phys_id}{'dies'}{$die_id}{'clusters'}{$cluster_id}{'cores'},
+ $counts,
+ $i);
+ }
+ }
+ }
+
}
- $item = main::message('undefined') if !defined $item;
}
- # print "$key_1 :: $key_2 :: $item\n";
- if ($b_main){
- $working->{'data'}{$id} = $item;
+ $i++;
+ }
+ $counts->{'struct-max'} = 1 if scalar @max > 1;
+ $counts->{'struct-min'} = 1 if scalar @min > 1;
+ $counts->{'struct-cores'} = 1 if scalar @phys_cores > 1;
+ if ($b_log){
+ main::log_data('dump','%$caches',$caches);
+ main::log_data('dump','%$counts',$counts);
+ }
+ if ($dbg[68]){
+ print '%$cpu: ', Data::Dumper::Dumper $cpu;
+ print 'cpu %$caches: ', Data::Dumper::Dumper $caches;
+ print 'cpu %$counts: ', Data::Dumper::Dumper $counts;
+ }
+ eval $end if $b_log;
+}
+
+# args: 0: $cpu by ref; 1: $counts by ref; 2: $phy_cpu by ref; 3: $i
+sub cp_dies_clusters {
+ eval $start if $b_log;
+ my ($cpu,$counts,$phys_cpu,$data,$i,$b_skip) = @_;
+ # we don't want the placeholder die ID counted as an actual die!
+ if ($phys_cpu->{'dies'} && $data->{'die-file'}){
+ $counts->{'cpu-topo'}[$i]{'dies'} = scalar keys %{$phys_cpu->{'dies'}};
+ $cpu->{'dies-count'} = $counts->{'cpu-topo'}[$i]{'dies'};
+ }
+ foreach my $id (sort keys %{$phys_cpu->{'dies'}}){
+ if ($phys_cpu->{'dies'}{$id}{'clusters'}){
+ # this will show dies x clusters in output, since no way to know if cluster
+ # ids are per phys cpu or per die.
+ $cpu->{'clusters-count'} = scalar keys %{$phys_cpu->{'dies'}{$id}{'clusters'}};
+ $counts->{'cpu-topo'}[$i]{'clusters'} = $cpu->{'clusters-count'};
+ foreach my $cluster_id (sort keys %{$phys_cpu->{'dies'}{$id}{'clusters'}}){
+ if ($phys_cpu->{'dies'}{$id}{'clusters'}{$cluster_id}{'cores'}){
+ cp_core_counter(
+ $cpu,
+ $phys_cpu->{'dies'}{$id}{'clusters'}{$cluster_id}{'cores'},
+ $counts,
+ $i,
+ $b_skip);
+ }
+ }
}
- elsif ($b_bug){
- my $type = ($item =~ /^Mitigation:/) ? 'mitigation': 'status';
- $item =~ s/Mitigation: //;
- $working->{'data'}{$id}{$key} = [$type,$item];
+ else {
+ if ($phys_cpu->{'dies'}{$id}{'cores'}){
+ cp_core_counter(
+ $cpu,
+ $phys_cpu->{'dies'}{$id}{'cores'},
+ $counts,
+ $i,
+ $b_skip);
+ }
}
- elsif ($b_cache){
- $working->{'cpus'}{$id}{$key}{$main_id}{$main_key} = $item;
+ }
+ eval $end if $b_log;
+}
+
+# args: 0: $cpu by ref; 1: $cores; 2: $counts (by ref); 3: $i;
+# 4: $b_dies: 0 for first iteration
+sub cp_core_counter {
+ eval $start if $b_log;
+ my ($cpu,$item,$counts,$i,$b_skip) = @_;
+ my $cores = 0;
+ $cores = scalar keys %{$item};
+ $counts->{'cpu-topo'}[$i]{'cores'} += $cores;
+ $cpu->{'cores'} = $cores;
+ $counts->{'cpu-cores'} += $cores if !$b_skip;
+ eval $end if $b_log;
+}
+
+# args: 0: cores hash; 1: $counts, by ref; 2: $i
+sub cp_set_threads {
+ eval $start if $b_log;
+ my ($cores,$counts,$i) = @_;
+ foreach my $core_key (sort keys %{$cores}){
+ if ((my $threads = scalar @{$cores->{$core_key}}) > 1){
+ $counts->{'cpu-topo'}[$i]{'cores-mt'}++;
+ $counts->{'cpu-topo'}[$i]{'threads'} += $threads;
+ # note: for mt+st type cpus, we need to handle tpc on output per type
+ $counts->{'cpu-topo'}[$i]{'tpc'} = $threads;
+ $counts->{'struct-mt'} = 1;
}
- elsif ($b_freq || $b_topo){
- $working->{'cpus'}{$id}{$key}{$main_key} = $item;
+ else {
+ $counts->{'cpu-topo'}[$i]{'cores-st'}++;
+ $counts->{'cpu-topo'}[$i]{'threads'}++;
+ $counts->{'struct-st'} = 1;
}
}
- main::log_data('dump','%$working',$working) if $b_log;
- print Data::Dumper::Dumper $working if $dbg[39];
eval $end if $b_log;
- return $working;
}
-# Set in one place to make sure we get them all consistent
-sub set_fake_data {
- $loaded{'cpu-fake-data'} = 1;
- my ($ci,$sys);
- ## CPUINFO DATA FILES ##
- ## ARM/MIPS
- # $ci = "$fake_data_dir/cpu/arm/arm-4-core-pinebook-1.txt";
- # $ci = "$fake_data_dir/cpu/arm/armv6-single-core-1.txt";
- # $ci = "$fake_data_dir/cpu/arm/armv7-dual-core-1.txt";
- # $ci = "$fake_data_dir/cpu/arm/armv7-new-format-model-name-single-core.txt";
- # $ci = "$fake_data_dir/cpu/arm/arm-2-die-96-core-rk01.txt";
- # $ci = "$fake_data_dir/cpu/arm/arm-shevaplug-1.2ghz.txt";
- # $ci = "$fake_data_dir/cpu/mips/mips-mainusg-cpuinfo.txt";
- # $ci = "$fake_data_dir/cpu/ppc/ppc-debian-ppc64-cpuinfo.txt";
- ## x86
- # $ci = "$fake_data_dir/cpu/amd/16-core-32-mt-ryzen.txt";
- # $ci = "$fake_data_dir/cpu/amd/2-16-core-epyc-abucodonosor.txt";
- # $ci = "$fake_data_dir/cpu/amd/2-core-probook-antix.txt";
- # $ci = "$fake_data_dir/cpu/amd/4-core-jean-antix.txt";
- # $ci = "$fake_data_dir/cpu/amd/4-core-althlon-mjro.txt";
- # $ci = "$fake_data_dir/cpu/amd/4-core-apu-vc-box.txt";
- # $ci = "$fake_data_dir/cpu/amd/4-core-a10-5800k-1.txt";
- # $ci = "$fake_data_dir/cpu/intel/1-core-486-fourtysixandtwo.txt";
- # $ci = "$fake_data_dir/cpu/intel/2-core-ht-atom-bruh.txt";
- # $ci = "$fake_data_dir/cpu/intel/core-2-i3.txt";
- # $ci = "$fake_data_dir/cpu/intel/8-core-i7-damentz64.txt";
- # $ci = "$fake_data_dir/cpu/intel/2-10-core-xeon-ht.txt";
- # $ci = "$fake_data_dir/cpu/intel/4-core-xeon-fake-dual-die-zyanya.txt";
- # $ci = "$fake_data_dir/cpu/intel/2-core-i5-fake-dual-die-hek.txt";
- # $ci = "$fake_data_dir/cpu/intel/2-1-core-xeon-vm-vs2017.txt";
- # $ci = "$fake_data_dir/cpu/intel/4-1-core-xeon-vps-frodo1.txt";
- # $ci = "$fake_data_dir/cpu/intel/4-6-core-xeon-no-mt-lathander.txt";
- ## Elbrus
- # $cpu_type = 'elbrus'; # uncomment to test elbrus
- # $ci = "$fake_data_dir/cpu/elbrus/elbrus-2c3/cpuinfo.txt";
- # $ci = "$fake_data_dir/cpu/elbrus/1xE1C-8.txt";
- # $ci = "$fake_data_dir/cpu/elbrus/1xE2CDSP-4.txt";
- # $ci = "$fake_data_dir/cpu/elbrus/1xE2S4-3-monocub.txt";
- # $ci = "$fake_data_dir/cpu/elbrus/1xMBE8C-7.txt";
- # $ci = "$fake_data_dir/cpu/elbrus/4xEL2S4-3.txt";
- # $ci = "$fake_data_dir/cpu/elbrus/4xE8C-7.txt";
- # $ci = "$fake_data_dir/cpu/elbrus/4xE2CDSP-4.txt";
- # $ci = "$fake_data_dir/cpu/elbrus/cpuinfo.e8c2.txt";
- ## Loongson
- # $cpu_type = 'elbrus'; # uncomment to test loongson
- $ci = "$fake_data_dir/cpu/loongson/3A5000M-4-core-4.19.0.txt";
-
- ## CPU CPUINFO/SYS PAIRS DATA FILES ##
- # $ci = "$fake_data_dir/cpu/sys-ci-pairs/android-pocom3-fake-cpuinfo.txt";
- # $sys = "$fake_data_dir/cpu/sys-ci-pairs/android-pocom3-fake-sys.txt";
- # $ci = "$fake_data_dir/cpu/sys-ci-pairs/arm-pine64-cpuinfo-1.txt";v
- # $sys = "$fake_data_dir/cpu/sys-ci-pairs/arm-pine64-sys-1.txt";
- # $ci = "$fake_data_dir/cpu/sys-ci-pairs/arm-riscyslack2-cpuinfo-1.txt";
- # $sys = "$fake_data_dir/cpu/sys-ci-pairs/arm-riscyslack2-sys-1.txt";
- # $ci = "$fake_data_dir/cpu/sys-ci-pairs/ppc-stuntkidz~cpuinfo.txt";
- # $sys = "$fake_data_dir/cpu/sys-ci-pairs/ppc-stuntkidz~sys.txt";
- # $ci = "$fake_data_dir/cpu/sys-ci-pairs/riscv-unmatched-2021~cpuinfo-1.txt";
- # $sys = "$fake_data_dir/cpu/sys-ci-pairs/riscv-unmatched-2021~sys-1.txt";
- # $ci = "$fake_data_dir/cpu/sys-ci-pairs/x86-brickwizard-atom-n270~cpuinfo-1.txt";
- # $sys = "$fake_data_dir/cpu/sys-ci-pairs/x86-brickwizard-atom-n270~sys-1.txt";
- # $ci = "$fake_data_dir/cpu/sys-ci-pairs/x86-amd-phenom-chrisretusn-cpuinfo-1.txt";
- # $sys = "$fake_data_dir/cpu/sys-ci-pairs/x86-amd-phenom-chrisretusn-sys-1.txt";
- # $ci = "$fake_data_dir/cpu/sys-ci-pairs/x86-drgibbon-intel-i7-cpuinfo.txt";
- # $sys = "$fake_data_dir/cpu/sys-ci-pairs/x86-drgibbon-intel-i7-sys.txt";
- # $ci = "$fake_data_dir/cpu/sys-ci-pairs/ryzen-threadripper-1x64-3950x-cpuinfo.txt";
- # $sys = "$fake_data_dir/cpu/sys-ci-pairs/ryzen-threadripper-1x64-3950x-sys.txt";
- # $ci = "$fake_data_dir/cpu/sys-ci-pairs/amd-threadripper-1x12-5945wx-cpuinfo-1.txt";
- # $sys = "$fake_data_dir/cpu/sys-ci-pairs/amd-threadripper-1x12-5945wx-sys-1.txt";
- # $ci = "$fake_data_dir/cpu/sys-ci-pairs/intel-i7-1165G7-4-core-no-smt-cpuinfo.txt";
- # $sys = "$fake_data_dir/cpu/sys-ci-pairs/intel-i7-1165G7-4-core-no-smt-sys.txt";
- # $ci = "$fake_data_dir/cpu/sys-ci-pairs/elbrus-e16c-1-cpuinfo.txt";
- # $sys = "$fake_data_dir/cpu/sys-ci-pairs/elbrus-e16c-1-sys.txt";
- $fake_data{'cpuinfo'} = $ci;
- $fake_data{'sys'} = $sys;
+sub cp_sys_caches {
+ eval $start if $b_log;
+ my ($sys_caches,$caches,$id,$id_di) = @_;
+ my $cache_id = ($id_di) ? $id_di: $id;
+ my %cache_desc;
+ if ($sys_caches->{$cache_id}){
+ # print Data::Dumper::Dumper $cpu_sys->{'cpus'};
+ foreach (@{$sys_caches->{$cache_id}}){
+ # android seen to have cache data without size item
+ next if !defined $_;
+ $caches->{$cache_id} += $_;
+ $cache_desc{$_}++ if $b_admin;
+ }
+ $caches->{$id} += $caches->{$id_di} if $id_di;
+ $caches->{$cache_id . '-desc'} = cp_cache_desc(\%cache_desc) if $b_admin;
+ }
+ eval $end if $b_log;
}
+## END SYS DATA ##
-sub sysctl_data {
+## BSD DATA
+sub cpu_sysctl_data {
eval $start if $b_log;
my ($cpu,@line,%speeds,@working);
my ($sep) = ('');
@@ -10366,7 +10635,7 @@ sub sysctl_data {
}
}
if (!$cpu->{'flags'} || !$cpu->{'family'}){
- my $dmesg_boot = dboot_data();
+ my $dmesg_boot = cp_dboot_data();
# this core count may fix failed MT detection.
$cpu->{'cores'} = $dmesg_boot->{'cores'} if $dmesg_boot->{'cores'};
$cpu->{'flags'} = $dmesg_boot->{'flags'} if !$cpu->{'flags'};
@@ -10385,13 +10654,12 @@ sub sysctl_data {
$cpu->{'type'} = $dmesg_boot->{'type'} if !$cpu->{'type'};
}
main::log_data('dump','%$cpu',$cpu) if $b_log;
- print Data::Dumper::Dumper $cpu if $dbg[8];
+ print 'sysctl $cpu: ', Data::Dumper::Dumper $cpu if $dbg[8];
eval $end if $b_log;
return $cpu;
}
-## DATA GENERATOR DATA SOURCES ##
-sub dboot_data {
+sub cp_dboot_data {
eval $start if $b_log;
my ($max_freq,$min_freq,@scalings);
my ($family,$flags,$microcode,$model,$sep,$stepping,$type) = ('','','','','','','');
@@ -10505,12 +10773,14 @@ sub dboot_data {
'stepping' => $stepping,
'type' => $type,
};
- print Data::Dumper::Dumper $values if $dbg[27];
+ print 'dboot $values: ', Data::Dumper::Dumper $values if $dbg[27];
eval $end if $b_log;
return $values;
}
+## END BSD DATA ##
-sub dmidecode_data {
+## DMIDECODE DATA ##
+sub cpu_dmidecode_data {
eval $start if $b_log;
my $dmi_data = {'L1' => 0, 'L2' => 0,'L3' => 0, 'phys-cnt' => 0,
'ext-clock' => undef, 'socket' => undef, 'speed' => undef,
@@ -10608,12 +10878,61 @@ sub dmidecode_data {
$dmi_data->{'upgrade'} = $upgrade;
}
main::log_data('dump','%$dmi_data',$dmi_data) if $b_log;
- print Data::Dumper::Dumper $dmi_data if $dbg[27];
+ print 'dmidecode $dmi_data: ', Data::Dumper::Dumper $dmi_data if $dbg[27];
eval $end if $b_log;
return $dmi_data;
}
-## CPU PROPERTIES MAIN ##
+# everything is passed by reference so no need to return anything
+sub cp_data_dmi {
+ eval $start if $b_log;
+ my ($cpu,$dmi_data,$caches,$counts,$cache_check) = @_;
+ my $cpu_dmi = cpu_dmidecode_data();
+ # fix for bsds that do not show physical cpus, like openbsd
+ if ($cpu_dmi->{'phys-cnt'} && $counts->{'physical'} == 1 &&
+ $cpu_dmi->{'phys-cnt'} > 1){
+ $counts->{'physical'} = $cpu_dmi->{'phys-cnt'};
+ }
+ # We have to undef all the sys stuff to get back to the true dmidecode results
+ # Too many variants to treat one by one, just clear it out if forced.
+ undef $caches if $force{'dmidecode'};
+ # We don't want to use dmi L1/L2/L3 at all for non BSD systems unless forced
+ # because have seen totally gibberish dmidecode data for caches. /sys cache
+ # data preferred, more granular and basically consistently right.
+ # Only run for linux if no cache data found, but BSD use to fill in missing
+ # (we don't care about legacy errors for BSD since the data isn't adequate).
+ # legacy dmidecode cache data used the per cache value, NOT the per CPU total
+ # value like it does today. Which makes it impossible to know for sure if the
+ # given value is right (new, or if cache matched cpu total) or inadequate.
+ if ((!$bsd_type && !$caches->{'l1'} && !$caches->{'l2'} && !$caches->{'l3'}) ||
+ ($bsd_type && (!$caches->{'l1'} || !$caches->{'l2'} || !$caches->{'l3'}))){
+ # Newer dmi: cache type total per phys cpu; Legacy: raw cache size only
+ if ($cpu_dmi->{'l1'} && !$caches->{'l1'}){
+ $caches->{'l1'} = $cpu_dmi->{'l1'};
+ $$cache_check = main::message('note-check');
+ }
+ # note: bsds often won't have L2 catch data found yet, but bsd sysctl can
+ # have these values so let's check just in case. OpenBSD does have it often.
+ if ($cpu_dmi->{'l2'} && !$caches->{'l2'}){
+ $caches->{'l2'} = $cpu_dmi->{'l2'};
+ $$cache_check = main::message('note-check');
+ }
+ if ($cpu_dmi->{'l3'} && !$caches->{'l3'}){
+ $caches->{'l3'} = $cpu_dmi->{'l3'};
+ $$cache_check = main::message('note-check');
+ }
+ }
+ $dmi_data->{'max-speed'} = $cpu_dmi->{'max-speed'};
+ $dmi_data->{'socket'} = $cpu_dmi->{'socket'} if $cpu_dmi->{'socket'};
+ $dmi_data->{'upgrade'} = $cpu_dmi->{'upgrade'} if $cpu_dmi->{'upgrade'};
+ $dmi_data->{'speed'} = $cpu_dmi->{'speed'} if $cpu_dmi->{'speed'};
+ $dmi_data->{'ext-clock'} = $cpu_dmi->{'ext-clock'} if $cpu_dmi->{'ext-clock'};
+ $dmi_data->{'volts'} = $cpu_dmi->{'volts'} if $cpu_dmi->{'volts'};
+ eval $end if $b_log;
+}
+## END DMIDECODE DATA ##
+
+## CPU PROPERTIES ##
sub cpu_properties {
my ($cpu) = @_;
my ($cpu_sys,$arch_level);
@@ -10664,7 +10983,7 @@ sub cpu_properties {
if (%risc && $counts->{'dies'} > 1 &&
$counts->{'cpu-cores'} == $counts->{'dies'}){
$counts->{'dies'} = 1;
- $cpu->{'dies'} = 1;
+ $cpu->{'dies-count'} = 1;
}
if ($type eq 'full' && ($extra > 1 || ($bsd_type && !$cpu->{'l2-cache'}))){
cp_data_dmi(
@@ -10720,7 +11039,7 @@ sub cpu_properties {
$tests
);
my $topology = {};
- cp_cpu_topology($counts,$topology);
+ cp_topology($counts,$topology);
# print "$cpu->{'type'}\n";
# print Data::Dumper::Dumper $cpu;
my $arch = cp_cpu_arch(
@@ -10805,350 +11124,7 @@ sub cpu_properties {
return $cpu_properties;
}
-## CPU DATA ENGINES ##
-# everything is passed by reference so no need to return anything
-sub cp_data_dmi {
- eval $start if $b_log;
- my ($cpu,$dmi_data,$caches,$counts,$cache_check) = @_;
- my $cpu_dmi = dmidecode_data();
- # fix for bsds that do not show physical cpus, like openbsd
- if ($cpu_dmi->{'phys-cnt'} && $counts->{'physical'} == 1 &&
- $cpu_dmi->{'phys-cnt'} > 1){
- $counts->{'physical'} = $cpu_dmi->{'phys-cnt'};
- }
- # We have to undef all the sys stuff to get back to the true dmidecode results
- # Too many variants to treat one by one, just clear it out if forced.
- undef $caches if $force{'dmidecode'};
- # We don't want to use dmi L1/L2/L3 at all for non BSD systems unless forced
- # because have seen totally gibberish dmidecode data for caches. /sys cache
- # data preferred, more granular and basically consistently right.
- # Only run for linux if no cache data found, but BSD use to fill in missing
- # (we don't care about legacy errors for BSD since the data isn't adequate).
- # legacy dmidecode cache data used the per cache value, NOT the per CPU total
- # value like it does today. Which makes it impossible to know for sure if the
- # given value is right (new, or if cache matched cpu total) or inadequate.
- if ((!$bsd_type && !$caches->{'l1'} && !$caches->{'l2'} && !$caches->{'l3'}) ||
- ($bsd_type && (!$caches->{'l1'} || !$caches->{'l2'} || !$caches->{'l3'}))){
- # Newer dmi: cache type total per phys cpu; Legacy: raw cache size only
- if ($cpu_dmi->{'l1'} && !$caches->{'l1'}){
- $caches->{'l1'} = $cpu_dmi->{'l1'};
- $$cache_check = main::message('note-check');
- }
- # note: bsds often won't have L2 catch data found yet, but bsd sysctl can
- # have these values so let's check just in case. OpenBSD does have it often.
- if ($cpu_dmi->{'l2'} && !$caches->{'l2'}){
- $caches->{'l2'} = $cpu_dmi->{'l2'};
- $$cache_check = main::message('note-check');
- }
- if ($cpu_dmi->{'l3'} && !$caches->{'l3'}){
- $caches->{'l3'} = $cpu_dmi->{'l3'};
- $$cache_check = main::message('note-check');
- }
- }
- $dmi_data->{'max-speed'} = $cpu_dmi->{'max-speed'};
- $dmi_data->{'socket'} = $cpu_dmi->{'socket'} if $cpu_dmi->{'socket'};
- $dmi_data->{'upgrade'} = $cpu_dmi->{'upgrade'} if $cpu_dmi->{'upgrade'};
- $dmi_data->{'speed'} = $cpu_dmi->{'speed'} if $cpu_dmi->{'speed'};
- $dmi_data->{'ext-clock'} = $cpu_dmi->{'ext-clock'} if $cpu_dmi->{'ext-clock'};
- $dmi_data->{'volts'} = $cpu_dmi->{'volts'} if $cpu_dmi->{'volts'};
- eval $end if $b_log;
-}
-
-sub cp_data_fallback {
- eval $start if $b_log;
- my ($cpu,$caches,$cache_check,$counts,$tests) = @_;
- if (!$counts->{'physical'}){
- # handle case where cpu reports say, phys id 0, 2, 4, 6
- foreach (@{$cpu->{'ids'}}){
- $counts->{'physical'}++ if $_;
- }
- }
- # count unique processors ##
- # note, this fails for intel cpus at times
- # print ref $cpu->{'processors'}, "\n";
- if (!$counts->{'processors'}){
- $counts->{'processors'} = scalar @{$cpu->{'processors'}};
- }
- # print "p count:$counts->{'processors'}\n";
- # print Data::Dumper::Dumper $cpu->{'processors'};
- # $counts->{'cpu-cores'} is per physical cpu
- # note: elbrus supports turning off cores, so we need to add one for cases
- # where rounds to 0 or 1 less
- # print "$cpu{'type'},$cpu{'family'},$cpu{'model-id'},$cpu{'arch'}\n";
- if ($tests->{'elbrus'} && $counts->{'processors'}){
- my $elbrus = cp_elbrus_data($cpu->{'family'},$cpu->{'model-id'},
- $counts->{'processors'},$cpu->{'arch'});
- $counts->{'cpu-cores'} = $elbrus->[0];
- $counts->{'physical'} = $elbrus->[1];
- $cpu->{'arch'} = $elbrus->[2];
- # print 'model id: ' . $cpu->{'model-id'} . ' arch: ' . $cpu->{'arch'} . " cpc: $counts->{'cpu-cores'} phyc: $counts->{'physical'} proc: $counts->{'processors'} \n";
- }
- $counts->{'physical'} ||= 1; # assume 1 if no id found, as with ARM
- foreach my $die_ref (@{$cpu->{'ids'}}){
- next if ref $die_ref ne 'ARRAY';
- $counts->{'cores'} = 0;
- $counts->{'dies'} = scalar @$die_ref;
- #$cpu->{'dies'} = $counts->{'dies'};
- foreach my $core_ref (@$die_ref){
- next if ref $core_ref ne 'ARRAY';
- $counts->{'cores'} = 0;# reset for each die!!
- # NOTE: the counters can be undefined because the index comes from
- # core id: which can be 0 skip 1 then 2, which leaves index 1 undefined
- # risc cpus do not actually show core id so ignore that counter
- foreach my $id (@$core_ref){
- $counts->{'cores'}++ if defined $id && !%risc;
- }
- # print 'cores: ' . $counts->{'cores'}, "\n";
- }
- }
- # this covers potentially cases where ARM cpus have > 1 die
- # maybe applies to all risc, not sure, but dies is broken anyway for cpuinfo
- if (!$cpu->{'dies'}){
- if ($risc{'arm'} && $counts->{'dies'} <= 1 && $cpu->{'dies'} > 1){
- $counts->{'dies'} = $cpu->{'dies'};
- }
- else {
- $cpu->{'dies'} = $counts->{'dies'};
- }
- }
- # this is an attempt to fix the amd family 15 bug with reported cores vs actual cores
- # NOTE: amd A6-4400M APU 2 core reports: cores: 1 siblings: 2
- # NOTE: AMD A10-5800K APU 4 core reports: cores: 2 siblings: 4
- if (!$counts->{'cpu-cores'}){
- if ($cpu->{'cores'} && !$counts->{'cores'} ||
- $cpu->{'cores'} >= $counts->{'cores'}){
- $counts->{'cpu-cores'} = $cpu->{'cores'};
- }
- elsif ($counts->{'cores'} > $cpu->{'cores'}){
- $counts->{'cpu-cores'} = $counts->{'cores'};
- }
- }
- # print "cpu-c:$counts->{'cpu-cores'}\n";
- # $counts->{'cpu-cores'} = $cpu->{'cores'};
- # like, intel core duo
- # NOTE: sadly, not all core intel are HT/MT, oh well...
- # xeon may show wrong core / physical id count, if it does, fix it. A xeon
- # may show a repeated core id : 0 which gives a fake num_of_cores=1
- if ($tests->{'intel'}){
- if ($cpu->{'siblings'} && $cpu->{'siblings'} > 1 &&
- $cpu->{'cores'} && $cpu->{'cores'} > 1){
- if ($cpu->{'siblings'}/$cpu->{'cores'} == 1){
- $tests->{'intel'} = 0;
- $tests->{'ht'} = 0;
- }
- else {
- $counts->{'cpu-cores'} = ($cpu->{'siblings'}/2);
- $tests->{'ht'} = 1;
- }
- }
- }
- # ryzen is made out of blocks of 2, 4, or 8 core dies...
- if ($tests->{'ryzen'}){
- $counts->{'cpu-cores'} = $cpu->{'cores'};
- # note: posix ceil isn't present in Perl for some reason, deprecated?
- my $working = $counts->{'cpu-cores'} / 8;
- my @temp = split('\.', $working);
- $cpu->{'dies'} = ($temp[1] && $temp[1] > 0) ? $temp[0]++ : $temp[0];
- $counts->{'dies'} = $cpu->{'dies'};
- }
- # these always have 4 dies
- elsif ($tests->{'epyc'}){
- $counts->{'cpu-cores'} = $cpu->{'cores'};
- $counts->{'dies'} = $cpu->{'dies'} = 4;
- }
- # final check, override the num of cores value if it clearly is wrong
- # and use the raw core count and synthesize the total instead of real count
- if ($counts->{'cpu-cores'} == 0 &&
- $cpu->{'cores'} * $counts->{'physical'} > 1){
- $counts->{'cpu-cores'} = ($cpu->{'cores'} * $counts->{'physical'});
- }
- # last check, seeing some intel cpus and vms with intel cpus that do not show any
- # core id data at all, or siblings.
- if ($counts->{'cpu-cores'} == 0 && $counts->{'processors'} > 0){
- $counts->{'cpu-cores'} = $counts->{'processors'};
- }
- # this happens with BSDs which have very little cpu data available
- if ($counts->{'processors'} == 0 && $counts->{'cpu-cores'} > 0){
- $counts->{'processors'} = $counts->{'cpu-cores'};
- if ($bsd_type && ($tests->{'ht'} || $tests->{'amd-zen'}) &&
- $counts->{'cpu-cores'} > 2){
- $counts->{'cpu-cores'} = $counts->{'cpu-cores'}/2;;
- }
- my $count = $counts->{'processors'};
- $count-- if $count > 0;
- $cpu->{'processors'}[$count] = 0;
- # no way to get per processor speeds yet, so assign 0 to each
- # must be a numeric value. Could use raw speed from core 0, but
- # that would just be a hack.
- foreach (0 .. $count){
- $cpu->{'processors'}[$_] = 0;
- }
- }
- # so far only OpenBSD has a way to detect MT cpus, but Openbsd has disabled MT
- if ($bsd_type){
- if ($cpu->{'siblings'} &&
- $counts->{'cpu-cores'} && $counts->{'cpu-cores'} > 1){
- $counts->{'cores-multiplier'} = $counts->{'cpu-cores'};
- }
- # if no siblings we couldn't get MT status of cpu so can't trust cache
- else {
- $$cache_check = main::message('note-check');
- }
- }
- # only elbrus shows L1 / L3 cache data in cpuinfo, cpu_sys data should show
- # for newer full linux.
- elsif ($counts->{'cpu-cores'} &&
- ($tests->{'elbrus'} || $counts->{'cpu-cores'} > 1)) {
- $counts->{'cores-multiplier'} = $counts->{'cpu-cores'};
- }
- # last test to catch some corner cases
- # seen a case where a xeon vm in a dual xeon system actually had 2 cores, no MT
- # so it reported 4 siblings, 2 cores, but actually only had 1 core per virtual cpu
- # print "prc: $counts->{'processors'} phc: $counts->{'physical'} coc: $counts->{'cores'} cpc: $counts->{'cpu-cores'}\n";
- # this test was for arm but I think it applies to all risc, but risc will be sys
- if (!%risc &&
- $counts->{'processors'} == $counts->{'physical'} * $counts->{'cores'} &&
- $counts->{'cpu-cores'} > $counts->{'cores'}){
- $tests->{'ht'} = 0;
- # $tests->{'xeon'} = 0;
- $tests->{'intel'} = 0;
- $counts->{'cpu-cores'} = 1;
- $counts->{'cores'} = 1;
- $cpu->{'siblings'} = 1;
- }
- eval $end if $b_log;
-}
-
-# all values passed by reference so no need for returns
-sub cp_data_sys {
- eval $start if $b_log;
- my ($cpu,$cpu_sys,$caches,$counts) = @_;
- my (@keys) = (sort keys %{$cpu_sys->{'cpus'}});
- return if !@keys;
- $counts->{'physical'} = scalar @keys;
- if ($type eq 'full' && $cpu_sys->{'cpus'}{$keys[0]}{'caches'}){
- cp_sys_caches($cpu_sys->{'cpus'}{$keys[0]}{'caches'},$caches,'l1','l1d');
- cp_sys_caches($cpu_sys->{'cpus'}{$keys[0]}{'caches'},$caches,'l1','l1i');
- cp_sys_caches($cpu_sys->{'cpus'}{$keys[0]}{'caches'},$caches,'l2','');
- cp_sys_caches($cpu_sys->{'cpus'}{$keys[0]}{'caches'},$caches,'l3','');
- }
- if ($cpu_sys->{'data'}{'speeds'}{'all'}){
- $counts->{'processors'} = scalar @{$cpu_sys->{'data'}{'speeds'}{'all'}};
- }
- if (defined $cpu_sys->{'data'}{'smt-active'}){
- if ($cpu_sys->{'data'}{'smt-active'}){
- $cpu->{'smt'} = 'enabled';
- }
- # values: on/off/notsupported/notimplemented
- elsif (defined $cpu_sys->{'data'}{'smt-control'} &&
- $cpu_sys->{'data'}{'smt-control'} =~ /^not/){
- $cpu->{'smt'} = main::message('unsupported');
- }
- else {
- $cpu->{'smt'} = 'disabled';
- }
- }
- my $i = 0;
- my (@governor,@max,@min,@phys_cores);
- foreach my $phys_id (@keys){
- if ($cpu_sys->{'cpus'}{$phys_id}{'cores'}){
- my ($mt,$st) = (0,0);
- my (@core_keys) = keys %{$cpu_sys->{'cpus'}{$phys_id}{'cores'}};
- $cpu->{'cores'} = $counts->{'cpu-cores'} = scalar @core_keys;
- $counts->{'cpu-topo'}[$i]{'cores'} = $cpu->{'cores'};
- if ($cpu_sys->{'cpus'}{$phys_id}{'dies'}){
- $counts->{'cpu-topo'}[$i]{'dies'} = scalar @{$cpu_sys->{'cpus'}{$phys_id}{'dies'}};
- $cpu->{'dies'} = $counts->{'cpu-topo'}[$i]{'dies'};
- }
- # If we ever get > 1 min/max speed per phy cpu, we'll need to fix the [0]
- if ($cpu_sys->{'cpus'}{$phys_id}{'max-freq'}[0]){
- if (!grep {$cpu_sys->{'cpus'}{$phys_id}{'max-freq'}[0] eq $_} @max){
- push(@max,$cpu_sys->{'cpus'}{$phys_id}{'max-freq'}[0]);
- }
- $counts->{'cpu-topo'}[$i]{'max'} = $cpu_sys->{'cpus'}{$phys_id}{'max-freq'}[0];
- }
- if ($cpu_sys->{'cpus'}{$phys_id}{'min-freq'}[0]){
- if (!grep {$cpu_sys->{'cpus'}{$phys_id}{'min-freq'}[0] eq $_} @min){
- push(@min,$cpu_sys->{'cpus'}{$phys_id}{'min-freq'}[0]);
- }
- $counts->{'cpu-topo'}[$i]{'min'} = $cpu_sys->{'cpus'}{$phys_id}{'min-freq'}[0];
- }
- # cheating, this is not a count, but we need the data for topology, must
- # sort since governors can be in different order if > 1
- if ($cpu_sys->{'cpus'}{$phys_id}{'governor'}){
- foreach my $gov (@{$cpu_sys->{'cpus'}{$phys_id}{'governor'}}){
- push(@governor,$gov) if !grep {$_ eq $gov} @governor;
- }
- $cpu->{'governor'} = join(',',@governor);
- }
- if ($cpu_sys->{'cpus'}{$phys_id}{'scaling-driver'}){
- $cpu->{'scaling-driver'} = $cpu_sys->{'cpus'}{$phys_id}{'scaling-driver'};
- }
- if ($cpu_sys->{'cpus'}{$phys_id}{'scaling-driver'}){
- $cpu->{'scaling-driver'} = $cpu_sys->{'cpus'}{$phys_id}{'scaling-driver'};
- }
- if ($cpu_sys->{'cpus'}{$phys_id}{'scaling-max-freq'}){
- $cpu->{'scaling-max-freq'} = $cpu_sys->{'cpus'}{$phys_id}{'scaling-max-freq'};
- }
- if ($cpu_sys->{'cpus'}{$phys_id}{'scaling-min-freq'}){
- $cpu->{'scaling-min-freq'} = $cpu_sys->{'cpus'}{$phys_id}{'scaling-min-freq'};
- }
- if (!grep {$counts->{'cpu-cores'} eq $_} @phys_cores){
- push(@phys_cores,$counts->{'cpu-cores'});
- }
- if ($counts->{'processors'}){
- if ($counts->{'processors'} > $counts->{'cpu-cores'}){
- for my $key (@core_keys){
- if ((my $threads = scalar @{$cpu_sys->{'cpus'}{$phys_id}{'cores'}{$key}}) > 1){
- $counts->{'cpu-topo'}[$i]{'cores-mt'}++;
- $counts->{'cpu-topo'}[$i]{'threads'} += $threads;
- # note: for mt+st type cpus, we need to handle tpc on output per type
- $counts->{'cpu-topo'}[$i]{'tpc'} = $threads;
- $counts->{'struct-mt'} = 1;
- }
- else {
- $counts->{'cpu-topo'}[$i]{'cores-st'}++;
- $counts->{'cpu-topo'}[$i]{'threads'}++;
- $counts->{'struct-st'} = 1;
- }
- }
- }
- }
- $i++;
- }
- }
- $counts->{'struct-max'} = 1 if scalar @max > 1;
- $counts->{'struct-min'} = 1 if scalar @min > 1;
- $counts->{'struct-cores'} = 1 if scalar @phys_cores > 1;
- if ($b_log){
- main::log_data('dump','%cpu_properties',$caches);
- main::log_data('dump','%cpu_properties',$counts);
- }
- # print Data::Dumper::Dumper $caches;
- # print Data::Dumper::Dumper $counts;
- eval $end if $b_log;
-}
-
-sub cp_sys_caches {
- eval $start if $b_log;
- my ($sys_caches,$caches,$id,$id_di) = @_;
- my $cache_id = ($id_di) ? $id_di: $id;
- my %cache_desc;
- if ($sys_caches->{$cache_id}){
- # print Data::Dumper::Dumper $cpu_sys->{'cpus'};
- foreach (@{$sys_caches->{$cache_id}}){
- # android seen to have cache data without size item
- next if !defined $_;
- $caches->{$cache_id} += $_;
- $cache_desc{$_}++ if $b_admin;
- }
- $caches->{$id} += $caches->{$id_di} if $id_di;
- $caches->{$cache_id . '-desc'} = cp_cache_desc(\%cache_desc) if $b_admin;
- }
- eval $end if $b_log;
-}
-
-## CPU PROPERTIES TOOLS ##
+## CP TOOLS
sub cp_cache_desc {
my ($cache_desc) = @_;
my ($desc,$sep) = ('','');
@@ -11232,6 +11208,358 @@ sub cp_caches_fallback {
eval $end if $b_log;
}
+sub cp_cores_alpha {
+ my $cores = $_[0];
+ my $string = '';
+ if ($cores > 4){
+ $string = $cores . '-core';
+ }
+ elsif ($cores == 0){
+ $string = main::message('unknown-cpu-topology');
+ }
+ else {
+ my @alpha = qw(single dual triple quad);
+ $string = $alpha[$cores-1] . ' core';
+ }
+ return $string;
+}
+
+# Only AMD/Intel 64 bit cpus
+sub cp_cpu_level {
+ eval $start if $b_log;
+ my %flags = map {$_ =>1} split(/\s+/,$_[0]);
+ my ($level,$note,@found);
+ # note, each later cpu level must contain all subsequent cpu flags
+ # baseline: all x86_64 cpus lm cmov cx8 fpu fxsr mmx syscall sse2
+ my @l1 = qw(cmov cx8 fpu fxsr lm mmx syscall sse2);
+ my @l2 = qw(cx16 lahf_lm popcnt sse4_1 sse4_2 ssse3);
+ my @l3 = qw(abm avx avx2 bmi1 bmi2 f16c fma movbe xsave);
+ my @l4 = qw(avx512f avx512bw avx512cd avx512dq avx512vl);
+ if ((@found = grep {$flags{$_}} @l1) && scalar(@found) == scalar(@l1)){
+ $level = 'v1';
+ # print 'v1: ', Data::Dumper::Dumper \@found;
+ if ((@found = grep {$flags{$_}} @l2) && scalar(@found) == scalar(@l2)){
+ $level = 'v2';
+ # print 'v2: ', Data::Dumper::Dumper \@found;
+ # It's not 100% certain that if flags exist v3/v4 supported. flags don't
+ # give full possible outcomes in these cases. See: docs/inxi-cpu.txt
+ if ((@found = grep {$flags{$_}} @l3) && scalar(@found) == scalar(@l3)){
+ $level = 'v3';
+ # print 'v3: ', Data::Dumper::Dumper \@found;
+ $note = main::message('note-check');
+ if ((@found = grep {$flags{$_}} @l4) && scalar(@found) == scalar(@l4)){
+ $level = 'v4';
+ # print 'v4: ', Data::Dumper::Dumper \@found;
+ }
+ }
+ }
+ }
+ $level = [$level,$note] if $level;
+ eval $end if $b_log;
+ return $level;
+}
+
+# Logic:
+# if > 1 processor && processor id (physical id) == core id then Multi threaded (MT)
+# if siblings > 1 && siblings == 2 * num_of_cores ($cpu->{'cores'}) then Multi threaded (MT)
+# if > 1 processor && processor id (physical id) != core id then Multi-Core Processors (MCP)
+# if > 1 processor && processor ids (physical id) > 1 then Symmetric Multi Processing (SMP)
+# if = 1 processor then single core/processor Uni-Processor (UP)
+sub cp_cpu_type {
+ eval $start if $b_log;
+ my ($counts,$cpu,$tests) = @_;
+ my $cpu_type = '';
+ if ($counts->{'processors'} > 1 ||
+ (defined $tests->{'intel'} && $tests->{'intel'} && $cpu->{'siblings'} > 0)){
+ # cpu_sys detected MT
+ if ($counts->{'struct-mt'}){
+ if ($counts->{'struct-mt'} && $counts->{'struct-st'}){
+ $cpu_type .= 'MST';
+ }
+ else {
+ $cpu_type .= 'MT';
+ }
+ }
+ # handle case of OpenBSD that has hw.smt but no other meaningful topology
+ elsif ($cpu->{'smt'}){
+ $cpu_type .= 'MT' if $cpu->{'smt'} eq 'enabled';
+ }
+ # non-multicore MT, with 2 or more threads per core
+ elsif ($counts->{'processors'} && $counts->{'physical'} &&
+ $counts->{'cpu-cores'} &&
+ $counts->{'processors'}/($counts->{'physical'} * $counts->{'cpu-cores'}) >= 2){
+ # print "mt:1\n";
+ $cpu_type .= 'MT';
+ }
+ # 2 or more siblings per cpu real core
+ elsif ($cpu->{'siblings'} > 1 && $cpu->{'siblings'}/$counts->{'cpu-cores'} >= 2){
+ # print "mt:3\n";
+ $cpu_type .= 'MT';
+ }
+ # non-MT multi-core or MT multi-core
+ if ($counts->{'cpu-cores'} > 1){
+ if ($counts->{'struct-mt'} && $counts->{'struct-st'}){
+ $cpu_type .= ' AMCP';
+ }
+ else {
+ $cpu_type .= ' MCP';
+ }
+ }
+ # only solidly known > 1 die cpus will use this
+ if (defined $cpu->{'dies'} && $cpu->{'dies'} > 1){
+ $cpu_type .= ' MCM';
+ }
+ # >1 cpu sockets active: Symetric Multi Processing
+ if ($counts->{'physical'} > 1){
+ if ($counts->{'struct-cores'} || $counts->{'struct-max'} ||
+ $counts->{'struct-min'}){
+ $cpu_type .= ' AMP';
+ }
+ else {
+ $cpu_type .= ' SMP';
+ }
+ }
+ $cpu_type =~ s/^\s+//;
+ }
+ else {
+ $cpu_type = 'UP';
+ }
+ eval $end if $b_log;
+ return $cpu_type;
+}
+
+sub cp_speed_data {
+ eval $start if $b_log;
+ my ($cpu,$cpu_sys) = @_;
+ my $info = {};
+ if (defined $cpu_sys->{'data'}){
+ if (defined $cpu_sys->{'data'}{'speeds'}{'min-freq'}){
+ $cpu->{'min-freq'} = $cpu_sys->{'data'}{'speeds'}{'min-freq'};
+ }
+ if (defined $cpu_sys->{'data'}{'speeds'}{'max-freq'}){
+ $cpu->{'max-freq'} = $cpu_sys->{'data'}{'speeds'}{'max-freq'};
+ }
+ if (defined $cpu_sys->{'data'}{'speeds'}{'scaling-min-freq'}){
+ $cpu->{'scaling-min-freq'} = $cpu_sys->{'data'}{'speeds'}{'scaling-min-freq'};
+ }
+ if (defined $cpu_sys->{'data'}{'speeds'}{'scaling-max-freq'}){
+ $cpu->{'scaling-max-freq'} = $cpu_sys->{'data'}{'speeds'}{'scaling-max-freq'};
+ }
+ # we don't need to see these if they are the same
+ if ($cpu->{'min-freq'} && $cpu->{'max-freq'} &&
+ $cpu->{'scaling-min-freq'} && $cpu->{'scaling-max-freq'} &&
+ $cpu->{'min-freq'} eq $cpu->{'scaling-min-freq'} &&
+ $cpu->{'max-freq'} eq $cpu->{'scaling-max-freq'}){
+ undef $cpu->{'scaling-min-freq'};
+ undef $cpu->{'scaling-max-freq'};
+ }
+ if (defined $cpu_sys->{'data'}{'speeds'}{'all'}){
+ # only replace if we got actual speed values from cpufreq, or if no legacy
+ # sourced processors data. Handles fake syz core speeds for counts.
+ if ((grep {$_} @{$cpu_sys->{'data'}{'speeds'}{'all'}}) ||
+ !@{$cpu->{'processors'}}){
+ $cpu->{'processors'} = $cpu_sys->{'data'}{'speeds'}{'all'};
+ }
+ }
+ if (defined $cpu_sys->{'data'}{'cpufreq-boost'}){
+ $cpu->{'boost'} = $cpu_sys->{'data'}{'cpufreq-boost'};
+ }
+ }
+ if (defined $cpu->{'processors'}){
+ if (scalar @{$cpu->{'processors'}} > 1){
+ my ($agg,$high) = (0,0);
+ for (@{$cpu->{'processors'}}){
+ next if !$_; # bsds might have 0 or undef value, that's junk
+ $agg += $_;
+ $high = $_ if $_ > $high;
+ }
+ if ($agg){
+ $cpu->{'avg-freq'} = int($agg/scalar @{$cpu->{'processors'}});
+ $cpu->{'cur-freq'} = $high;
+ $info->{'avg-speed-key'} = 'avg';
+ $info->{'speed'} = $cpu->{'avg-freq'};
+ if ($high > $cpu->{'avg-freq'}){
+ $cpu->{'high-freq'} = $high;
+ $info->{'high-speed-key'} = 'high';
+ }
+ }
+ }
+ elsif ($cpu->{'processors'}[0]) {
+ $cpu->{'cur-freq'} = $cpu->{'processors'}[0];
+ $info->{'speed'} = $cpu->{'cur-freq'};
+ }
+ }
+ # BSDs generally will have processors count, but not per core speeds
+ if ($cpu->{'cur-freq'} && !$info->{'speed'}){
+ $info->{'speed'} = $cpu->{'cur-freq'};
+ }
+ if ($cpu->{'min-freq'} || $cpu->{'max-freq'}){
+ ($info->{'min-max'},$info->{'min-max-key'}) = cp_speed_min_max(
+ $cpu->{'min-freq'},
+ $cpu->{'max-freq'});
+ }
+ if ($cpu->{'scaling-min-freq'} || $cpu->{'scaling-max-freq'}){
+ ($info->{'scaling-min-max'},$info->{'scaling-min-max-key'}) = cp_speed_min_max(
+ $cpu->{'scaling-min-freq'},
+ $cpu->{'scaling-max-freq'},
+ 'sc');
+ }
+ if ($cpu->{'cur-freq'}){
+ if ($show{'short'}){
+ $info->{'speed-key'} = 'speed';
+ }
+ elsif ($show{'cpu-basic'}){
+ $info->{'speed-key'} = 'speed (MHz)';
+ }
+ else {
+ $info->{'speed-key'} = 'Speed (MHz)';
+ }
+ }
+ eval $end if $b_log;
+ return $info;
+}
+
+sub cp_speed_min_max {
+ my ($min,$max,$type) = @_;
+ my ($min_max,$key);
+ if ($min && $max){
+ $min_max = "$min/$max";
+ $key = "min/max";
+ }
+ elsif ($max){
+ $min_max = $max;
+ $key = "max";
+ }
+ elsif ($min){
+ $min_max = $min;
+ $key = "min";
+ }
+ $key = $type . '-' . $key if $type && $key;
+ return ($min_max,$key);
+}
+
+# args: 0: cpu, by ref; 1: update $tests by reference
+sub cp_test_types {
+ my ($cpu,$tests) = @_;
+ if ($cpu->{'type'} eq 'intel'){
+ $$tests{'intel'} = 1;
+ $$tests{'xeon'} = 1 if $cpu->{'model_name'} =~ /Xeon/i;
+ }
+ elsif ($cpu->{'type'} eq 'amd'){
+ if ($cpu->{'family'} && $cpu->{'family'} eq '17'){
+ $$tests{'amd-zen'} = 1;
+ if ($cpu->{'model_name'}){
+ if ($cpu->{'model_name'} =~ /Ryzen/i){
+ $$tests{'ryzen'} = 1;
+ }
+ elsif ($cpu->{'model_name'} =~ /EPYC/i){
+ $$tests{'epyc'} = 1;
+ }
+ }
+ }
+ }
+ elsif ($cpu->{'type'} eq 'elbrus'){
+ $$tests{'elbrus'} = 1;
+ }
+}
+
+sub cp_topology {
+ my ($counts,$topology) = @_;
+ my @alpha = qw(Single Dual Triple Quad);
+ my ($sep) = ('');
+ my (%keys,%done);
+ my @tests = ('x'); # prefill [0] because iterator runs before 'next' test.
+ if ($counts->{'cpu-topo'}){
+ # first we want to find out how many of each physical variant there are
+ foreach my $topo (@{$counts->{'cpu-topo'}}){
+ # turn sorted hash into string
+ my $test = join('::', map{$_ . ':' . $topo->{$_}} sort keys %$topo);
+ if ($keys{$test}){
+ $keys{$test}++;
+ }
+ else {
+ $keys{$test} = 1;
+ }
+ push(@tests,$test);
+ }
+ my ($i,$j) = (0,0);
+ # then we build up the topology data per variant
+ foreach my $topo (@{$counts->{'cpu-topo'}}){
+ my $key = '';
+ $i++;
+ next if $done{$tests[$i]};
+ $done{$tests[$i]} = 1;
+ if ($b_admin && $type eq 'full'){
+ $topology->{'full'}[$j]{'cpus'} = $keys{$tests[$i]};
+ $topology->{'full'}[$j]{'cores'} = $topo->{'cores'};
+ if ($topo->{'threads'} && $topo->{'cores'} != $topo->{'threads'}){
+ $topology->{'full'}[$j]{'threads'} = $topo->{'threads'};
+ }
+ if ($topo->{'dies'}){
+ $topology->{'full'}[$j]{'dies-count'} = $topo->{'dies'};
+ }
+ if ($topo->{'clusters'}){
+ # clusters _should_ be per die, but it's not a guarantee
+ if ($topo->{'dies'} && $topo->{'dies'} > 1){
+ $topo->{'clusters'} = $topo->{'dies'} . 'x' . $topo->{'clusters'};
+ }
+ $topology->{'full'}[$j]{'clusters'} = $topo->{'clusters'};
+ }
+ if ($counts->{'struct-mt'}){
+ $topology->{'full'}[$j]{'cores-mt'} = $topo->{'cores-mt'};
+ }
+ if ($counts->{'struct-st'}){
+ $topology->{'full'}[$j]{'cores-st'} = $topo->{'cores-st'};
+ }
+ if ($counts->{'struct-max'} || $counts->{'struct-min'}){
+ $topology->{'full'}[$j]{'max'} = $topo->{'max'};
+ $topology->{'full'}[$j]{'min'} = $topo->{'min'};
+ }
+ if ($topo->{'smt'}){
+ $topology->{'full'}[$j]{'smt'} = $topo->{'smt'};
+ }
+ if ($topo->{'tpc'}){
+ $topology->{'full'}[$j]{'tpc'} = $topo->{'tpc'};
+ }
+ $j++;
+ }
+ else {
+ # start building string
+ $topology->{'string'} .= $sep;
+ $sep = ',';
+ if ($counts->{'physical'} > 1) {
+ my $phys = ($topology->{'struct-cores'}) ? $keys{$tests[$i]} : $counts->{'physical'};
+ $topology->{'string'} .= $phys . 'x ';
+ $topology->{'string'} .= $topo->{'cores'} . '-core';
+ }
+ else {
+ $topology->{'string'} .= cp_cores_alpha($topo->{'cores'});
+ }
+ # alder lake type cpu
+ if ($topo->{'cores-st'} && $topo->{'cores-mt'}){
+ $topology->{'string'} .= ' (' . $topo->{'cores-mt'} . '-mt/';
+ $topology->{'string'} .= $topo->{'cores-st'} . '-st)';
+ }
+ # we only want to show > 1 phys short form basic if cpus have different
+ # core counts, not different min/max frequencies
+ last if !$topology->{'struct-cores'};
+ }
+ }
+ }
+ else {
+ if ($counts->{'physical'} > 1) {
+ $topology->{'string'} = $counts->{'physical'} . 'x ';
+ $topology->{'string'} .= $counts->{'cpu-cores'} . '-core';
+ }
+ else {
+ $topology->{'string'} = cp_cores_alpha($counts->{'cpu-cores'});
+ }
+ }
+ $topology->{'string'} ||= '';
+}
+## END CP TOOLS
+## END CPU PROPERTIES ##
+
## START CPU ARCH ##
sub cp_cpu_arch {
eval $start if $b_log;
@@ -11464,13 +11792,13 @@ sub cp_cpu_arch {
# shares model 8 with zen, stepping unknown
elsif ($model =~ /^(8)$/){
$arch = 'Zen+';
- $gen = '2';
+ $gen = '1+';
$process = 'GF 12nm';
$year = '2018-21';}
# used this but it didn't age well: ^(2[0123456789ABCDEF]|
elsif ($model =~ /^(3.|4.|5.|6.|7.|8.|9.|A.)$/){
$arch = 'Zen 2';
- $gen = '3';
+ $gen = '2';
$process = 'TSMC n7 (7nm)'; # some consumer maybe GF 14nm
$year = '2020-22';}
else {
@@ -11492,26 +11820,26 @@ sub cp_cpu_arch {
# zen4c is for cloud hyperscale
if ($model =~ /^(78)$/){
$arch = 'Zen 4c';
- $gen = '5';
- $process = 'TSMC n4 (4nm)';
+ $gen = '4';
+ $process = 'TSMC n5 (5nm)'; # roadmapped to n4 originally
$year = '2023+';}
# ext model 6,7, base models trickling in
# 10 engineering sample
elsif ($model =~ /^(1.|6.|7.|A.)$/){
$arch = 'Zen 4';
- $gen = '5';
+ $gen = '4';
$process = 'TSMC n5 (5nm)';
$year = '2022+';}
# double check 40, 44; 21 confirmed
elsif ($model =~ /^(21|4.)$/){
$arch = 'Zen 3+';
- $gen = '4';
+ $gen = '3';
$process = 'TSMC n6 (7nm)';
$year = '2022';}
# 21, 50: step 0; known: 21, 3x, 50
elsif ($model =~ /^(0|1|8|2.|3.|5.)$/){
$arch = 'Zen 3';
- $gen = '4';
+ $gen = '3';
$process = 'TSMC n7 (7nm)';
$year = '2021-22';}
else {
@@ -11525,13 +11853,18 @@ sub cp_cpu_arch {
if ($model =~ /^(0)$/){
$arch = 'Zen 5';
$gen = '5';
- $process = 'TSMC n3 (3nm)'; # turin could be 4nm, need more data
+ $process = 'TSMC n4 (4nm)'; # turin could be 4nm, need more data
$year = '2023+';}
+ elsif ($model =~ /^(1.)$/){
+ $arch = 'Zen 5c';
+ $gen = '5';
+ $process = 'TSMC n3 (3nm)'; # turin could be 4nm, need more data
+ $year = '2024+';}
# Strix Point; Granite Ridge; Krackan Point; Strix Halo
- elsif ($model =~ /^(10|20|40|60|70)$/){
+ elsif ($model =~ /^(2.|4.|6.|7.)$/){
$arch = 'Zen 5';
$gen = '5';
- $process = 'TSMC n3 (3nm)'; # desktop, granite ridge, confirm 2024
+ $process = 'TSMC n4 (4nm)'; # desktop, granite ridge, confirm 2024
$year = '2024+';}
else {
$arch = 'Zen 5';
@@ -12013,7 +12346,11 @@ sub cp_cpu_arch {
$year = '2023+';}
elsif ($model =~ /^(AD|AE)$/){
$arch = 'Granite Rapids'; # ?
- $process = 'Intel 3 (7nm+)'; # confirm
+ $process = 'Intel 3 (7nm+)'; # listed with intel 3 and 7
+ $year = '2024+';}
+ elsif ($model =~ /^(AF)$/){
+ $arch = 'Sierra Forest'; # ?
+ $process = 'Intel 3 (5nm)';
$year = '2024+';}
elsif ($model =~ /^(B6)$/){
$arch = 'Grand Ridge'; # 14 gen
@@ -12025,10 +12362,10 @@ sub cp_cpu_arch {
$year = '2022+';}
elsif ($model =~ /^(BC|BD)$/){
$arch = 'Lunar Lake'; # 15 gn
- $process = 'Intel 18a (1.8nm)';
+ $process = 'TSMC n3b (3nm)'; # n6 controller tile. Announced w/intel 18a
$year = '2024+';} # seen APU IDs, so out there
# Meteor Lake-S maybe cancelled, replaced by arrow
- elsif ($model =~ /^(C5|C6)$/){
+ elsif ($model =~ /^(C5|C6|CA)$/){
$arch = 'Arrow Lake'; # 15 gen; igpu battleimage 3/4nm
# gfx tile is TSMC 3nm
$process = 'Intel 20a (2nm)';# TSMC 3nm (corei3-5)/Intel 20A 2nm (core i5-9)
@@ -12041,6 +12378,10 @@ sub cp_cpu_arch {
$arch = 'Emerald Rapids'; # 5th gen xeon
$process = 'Intel 7 (10nm)';
$year = '2023+';}
+ elsif ($model =~ /^(DD)$/){
+ $arch = 'Clearwater Forest';
+ $process = 'Intel 18a (1.8nm)';
+ $year = '2025+';}
## roadmaps: check and update, since Intel misses their targets often
# Sapphire Rapids: 13 gen (?), Intel 7 (10nm), 2023
# Emerald Rapids: Intel 7 (10nm), 2023
@@ -12195,212 +12536,170 @@ sub cp_cpu_arch {
}
## END CPU ARCH ##
-# Only AMD/Intel 64 bit cpus
-sub cp_cpu_level {
+## LEGACY CPU DATA ENGINE ##
+sub cp_data_fallback {
eval $start if $b_log;
- my %flags = map {$_ =>1} split(/\s+/,$_[0]);
- my ($level,$note,@found);
- # note, each later cpu level must contain all subsequent cpu flags
- # baseline: all x86_64 cpus lm cmov cx8 fpu fxsr mmx syscall sse2
- my @l1 = qw(cmov cx8 fpu fxsr lm mmx syscall sse2);
- my @l2 = qw(cx16 lahf_lm popcnt sse4_1 sse4_2 ssse3);
- my @l3 = qw(abm avx avx2 bmi1 bmi2 f16c fma movbe xsave);
- my @l4 = qw(avx512f avx512bw avx512cd avx512dq avx512vl);
- if ((@found = grep {$flags{$_}} @l1) && scalar(@found) == scalar(@l1)){
- $level = 'v1';
- # print 'v1: ', Data::Dumper::Dumper \@found;
- if ((@found = grep {$flags{$_}} @l2) && scalar(@found) == scalar(@l2)){
- $level = 'v2';
- # print 'v2: ', Data::Dumper::Dumper \@found;
- # It's not 100% certain that if flags exist v3/v4 supported. flags don't
- # give full possible outcomes in these cases. See: docs/inxi-cpu.txt
- if ((@found = grep {$flags{$_}} @l3) && scalar(@found) == scalar(@l3)){
- $level = 'v3';
- # print 'v3: ', Data::Dumper::Dumper \@found;
- $note = main::message('note-check');
- if ((@found = grep {$flags{$_}} @l4) && scalar(@found) == scalar(@l4)){
- $level = 'v4';
- # print 'v4: ', Data::Dumper::Dumper \@found;
- }
- }
+ my ($cpu,$caches,$cache_check,$counts,$tests) = @_;
+ if (!$counts->{'physical'}){
+ # handle case where cpu reports say, phys id 0, 2, 4, 6
+ foreach (@{$cpu->{'ids'}}){
+ $counts->{'physical'}++ if $_;
}
}
- $level = [$level,$note] if $level;
- eval $end if $b_log;
- return $level;
-}
-
-sub cp_cpu_topology {
- my ($counts,$topology) = @_;
- my @alpha = qw(Single Dual Triple Quad);
- my ($sep) = ('');
- my (%keys,%done);
- my @tests = ('x'); # prefill [0] because iterator runs before 'next' test.
- if ($counts->{'cpu-topo'}){
- # first we want to find out how many of each physical variant there are
- foreach my $topo (@{$counts->{'cpu-topo'}}){
- # turn sorted hash into string
- my $test = join('::', map{$_ . ':' . $topo->{$_}} sort keys %$topo);
- if ($keys{$test}){
- $keys{$test}++;
- }
- else {
- $keys{$test} = 1;
- }
- push(@tests,$test);
- }
- my ($i,$j) = (0,0);
- # then we build up the topology data per variant
- foreach my $topo (@{$counts->{'cpu-topo'}}){
- my $key = '';
- $i++;
- next if $done{$tests[$i]};
- $done{$tests[$i]} = 1;
- if ($b_admin && $type eq 'full'){
- $topology->{'full'}[$j]{'cpus'} = $keys{$tests[$i]};
- $topology->{'full'}[$j]{'cores'} = $topo->{'cores'};
- if ($topo->{'threads'} && $topo->{'cores'} != $topo->{'threads'}){
- $topology->{'full'}[$j]{'threads'} = $topo->{'threads'};
- }
- if ($topo->{'dies'} && $topo->{'dies'} > 1){
- $topology->{'full'}[$j]{'dies'} = $topo->{'dies'};
- }
- if ($counts->{'struct-mt'}){
- $topology->{'full'}[$j]{'cores-mt'} = $topo->{'cores-mt'};
- }
- if ($counts->{'struct-st'}){
- $topology->{'full'}[$j]{'cores-st'} = $topo->{'cores-st'};
- }
- if ($counts->{'struct-max'} || $counts->{'struct-min'}){
- $topology->{'full'}[$j]{'max'} = $topo->{'max'};
- $topology->{'full'}[$j]{'min'} = $topo->{'min'};
- }
- if ($topo->{'smt'}){
- $topology->{'full'}[$j]{'smt'} = $topo->{'smt'};
- }
- if ($topo->{'tpc'}){
- $topology->{'full'}[$j]{'tpc'} = $topo->{'tpc'};
- }
- $j++;
- }
- else {
- # start building string
- $topology->{'string'} .= $sep;
- $sep = ',';
- if ($counts->{'physical'} > 1) {
- my $phys = ($topology->{'struct-cores'}) ? $keys{$tests[$i]} : $counts->{'physical'};
- $topology->{'string'} .= $phys . 'x ';
- $topology->{'string'} .= $topo->{'cores'} . '-core';
- }
- else {
- $topology->{'string'} .= cp_cpu_alpha($topo->{'cores'});
- }
- # alder lake type cpu
- if ($topo->{'cores-st'} && $topo->{'cores-mt'}){
- $topology->{'string'} .= ' (' . $topo->{'cores-mt'} . '-mt/';
- $topology->{'string'} .= $topo->{'cores-st'} . '-st)';
- }
- # we only want to show > 1 phys short form basic if cpus have different
- # core counts, not different min/max frequencies
- last if !$topology->{'struct-cores'};
+ # count unique processors ##
+ # note, this fails for intel cpus at times
+ # print ref $cpu->{'processors'}, "\n";
+ if (!$counts->{'processors'}){
+ $counts->{'processors'} = scalar @{$cpu->{'processors'}};
+ }
+ # print "p count:$counts->{'processors'}\n";
+ # print Data::Dumper::Dumper $cpu->{'processors'};
+ # $counts->{'cpu-cores'} is per physical cpu
+ # note: elbrus supports turning off cores, so we need to add one for cases
+ # where rounds to 0 or 1 less
+ # print "$cpu{'type'},$cpu{'family'},$cpu{'model-id'},$cpu{'arch'}\n";
+ if ($tests->{'elbrus'} && $counts->{'processors'}){
+ my $elbrus = cp_elbrus_data($cpu->{'family'},$cpu->{'model-id'},
+ $counts->{'processors'},$cpu->{'arch'});
+ $counts->{'cpu-cores'} = $elbrus->[0];
+ $counts->{'physical'} = $elbrus->[1];
+ $cpu->{'arch'} = $elbrus->[2];
+ # print 'model id: ' . $cpu->{'model-id'} . ' arch: ' . $cpu->{'arch'} . " cpc: $counts->{'cpu-cores'} phyc: $counts->{'physical'} proc: $counts->{'processors'} \n";
+ }
+ $counts->{'physical'} ||= 1; # assume 1 if no id found, as with ARM
+ foreach my $die_ref (@{$cpu->{'ids'}}){
+ next if ref $die_ref ne 'ARRAY';
+ $counts->{'cores'} = 0;
+ $counts->{'dies'} = scalar @$die_ref;
+ #$cpu->{'dies-count'} = $counts->{'dies'};
+ foreach my $core_ref (@$die_ref){
+ next if ref $core_ref ne 'ARRAY';
+ $counts->{'cores'} = 0;# reset for each die!!
+ # NOTE: the counters can be undefined because the index comes from
+ # core id: which can be 0 skip 1 then 2, which leaves index 1 undefined
+ # risc cpus do not actually show core id so ignore that counter
+ foreach my $id (@$core_ref){
+ $counts->{'cores'}++ if defined $id && !%risc;
}
+ # print 'cores: ' . $counts->{'cores'}, "\n";
}
}
- else {
- if ($counts->{'physical'} > 1) {
- $topology->{'string'} = $counts->{'physical'} . 'x ';
- $topology->{'string'} .= $counts->{'cpu-cores'} . '-core';
+ # this covers potentially cases where ARM cpus have > 1 die
+ # maybe applies to all risc, not sure, but dies is broken anyway for cpuinfo
+ if (!$cpu->{'dies-count'}){
+ if ($risc{'arm'} && $counts->{'dies'} <= 1 && $cpu->{'dies-count'} > 1){
+ $counts->{'dies'} = $cpu->{'dies-count'};
}
else {
- $topology->{'string'} = cp_cpu_alpha($counts->{'cpu-cores'});
+ $cpu->{'dies-count'} = $counts->{'dies'};
}
}
- $topology->{'string'} ||= '';
-}
-
-sub cp_cpu_alpha {
- my $cores = $_[0];
- my $string = '';
- if ($cores > 4){
- $string = $cores . '-core';
- }
- elsif ($cores == 0){
- $string = main::message('unknown-cpu-topology');
- }
- else {
- my @alpha = qw(single dual triple quad);
- $string = $alpha[$cores-1] . ' core';
+ # this is an attempt to fix the amd family 15 bug with reported cores vs actual cores
+ # NOTE: amd A6-4400M APU 2 core reports: cores: 1 siblings: 2
+ # NOTE: AMD A10-5800K APU 4 core reports: cores: 2 siblings: 4
+ if (!$counts->{'cpu-cores'}){
+ if ($cpu->{'cores'} && !$counts->{'cores'} ||
+ $cpu->{'cores'} >= $counts->{'cores'}){
+ $counts->{'cpu-cores'} = $cpu->{'cores'};
+ }
+ elsif ($counts->{'cores'} > $cpu->{'cores'}){
+ $counts->{'cpu-cores'} = $counts->{'cores'};
+ }
}
- return $string;
-}
-
-# Logic:
-# if > 1 processor && processor id (physical id) == core id then Multi threaded (MT)
-# if siblings > 1 && siblings == 2 * num_of_cores ($cpu->{'cores'}) then Multi threaded (MT)
-# if > 1 processor && processor id (physical id) != core id then Multi-Core Processors (MCP)
-# if > 1 processor && processor ids (physical id) > 1 then Symmetric Multi Processing (SMP)
-# if = 1 processor then single core/processor Uni-Processor (UP)
-sub cp_cpu_type {
- eval $start if $b_log;
- my ($counts,$cpu,$tests) = @_;
- my $cpu_type = '';
- if ($counts->{'processors'} > 1 ||
- (defined $tests->{'intel'} && $tests->{'intel'} && $cpu->{'siblings'} > 0)){
- # cpu_sys detected MT
- if ($counts->{'struct-mt'}){
- if ($counts->{'struct-mt'} && $counts->{'struct-st'}){
- $cpu_type .= 'MST';
+ # print "cpu-c:$counts->{'cpu-cores'}\n";
+ # $counts->{'cpu-cores'} = $cpu->{'cores'};
+ # like, intel core duo
+ # NOTE: sadly, not all core intel are HT/MT, oh well...
+ # xeon may show wrong core / physical id count, if it does, fix it. A xeon
+ # may show a repeated core id : 0 which gives a fake num_of_cores=1
+ if ($tests->{'intel'}){
+ if ($cpu->{'siblings'} && $cpu->{'siblings'} > 1 &&
+ $cpu->{'cores'} && $cpu->{'cores'} > 1){
+ if ($cpu->{'siblings'}/$cpu->{'cores'} == 1){
+ $tests->{'intel'} = 0;
+ $tests->{'ht'} = 0;
}
else {
- $cpu_type .= 'MT';
+ $counts->{'cpu-cores'} = ($cpu->{'siblings'}/2);
+ $tests->{'ht'} = 1;
}
}
- # handle case of OpenBSD that has hw.smt but no other meaningful topology
- elsif ($cpu->{'smt'}){
- $cpu_type .= 'MT' if $cpu->{'smt'} eq 'enabled';
- }
- # non-multicore MT, with 2 or more threads per core
- elsif ($counts->{'processors'} && $counts->{'physical'} &&
- $counts->{'cpu-cores'} &&
- $counts->{'processors'}/($counts->{'physical'} * $counts->{'cpu-cores'}) >= 2){
- # print "mt:1\n";
- $cpu_type .= 'MT';
- }
- # 2 or more siblings per cpu real core
- elsif ($cpu->{'siblings'} > 1 && $cpu->{'siblings'}/$counts->{'cpu-cores'} >= 2){
- # print "mt:3\n";
- $cpu_type .= 'MT';
+ }
+ # ryzen is made out of blocks of 2, 4, or 8 core dies...
+ if ($tests->{'ryzen'}){
+ $counts->{'cpu-cores'} = $cpu->{'cores'};
+ # note: posix ceil isn't present in Perl for some reason, deprecated?
+ my $working = $counts->{'cpu-cores'} / 8;
+ my @temp = split('\.', $working);
+ $cpu->{'dies-count'} = ($temp[1] && $temp[1] > 0) ? $temp[0]++ : $temp[0];
+ $counts->{'dies'} = $cpu->{'dies-count'};
+ }
+ # these always have 4 dies
+ elsif ($tests->{'epyc'}){
+ $counts->{'cpu-cores'} = $cpu->{'cores'};
+ $counts->{'dies'} = $cpu->{'dies-count'} = 4;
+ }
+ # final check, override the num of cores value if it clearly is wrong
+ # and use the raw core count and synthesize the total instead of real count
+ if ($counts->{'cpu-cores'} == 0 &&
+ $cpu->{'cores'} * $counts->{'physical'} > 1){
+ $counts->{'cpu-cores'} = ($cpu->{'cores'} * $counts->{'physical'});
+ }
+ # last check, seeing some intel cpus and vms with intel cpus that do not show any
+ # core id data at all, or siblings.
+ if ($counts->{'cpu-cores'} == 0 && $counts->{'processors'} > 0){
+ $counts->{'cpu-cores'} = $counts->{'processors'};
+ }
+ # this happens with BSDs which have very little cpu data available
+ if ($counts->{'processors'} == 0 && $counts->{'cpu-cores'} > 0){
+ $counts->{'processors'} = $counts->{'cpu-cores'};
+ if ($bsd_type && ($tests->{'ht'} || $tests->{'amd-zen'}) &&
+ $counts->{'cpu-cores'} > 2){
+ $counts->{'cpu-cores'} = $counts->{'cpu-cores'}/2;;
}
- # non-MT multi-core or MT multi-core
- if ($counts->{'cpu-cores'} > 1){
- if ($counts->{'struct-mt'} && $counts->{'struct-st'}){
- $cpu_type .= ' AMCP';
- }
- else {
- $cpu_type .= ' MCP';
- }
+ my $count = $counts->{'processors'};
+ $count-- if $count > 0;
+ $cpu->{'processors'}[$count] = 0;
+ # no way to get per processor speeds yet, so assign 0 to each
+ # must be a numeric value. Could use raw speed from core 0, but
+ # that would just be a hack.
+ foreach (0 .. $count){
+ $cpu->{'processors'}[$_] = 0;
}
- # only solidly known > 1 die cpus will use this
- if ($cpu->{'dies'} > 1){
- $cpu_type .= ' MCM';
+ }
+ # so far only OpenBSD has a way to detect MT cpus, but Openbsd has disabled MT
+ if ($bsd_type){
+ if ($cpu->{'siblings'} &&
+ $counts->{'cpu-cores'} && $counts->{'cpu-cores'} > 1){
+ $counts->{'cores-multiplier'} = $counts->{'cpu-cores'};
}
- # >1 cpu sockets active: Symetric Multi Processing
- if ($counts->{'physical'} > 1){
- if ($counts->{'struct-cores'} || $counts->{'struct-max'} ||
- $counts->{'struct-min'}){
- $cpu_type .= ' AMP';
- }
- else {
- $cpu_type .= ' SMP';
- }
+ # if no siblings we couldn't get MT status of cpu so can't trust cache
+ else {
+ $$cache_check = main::message('note-check');
}
- $cpu_type =~ s/^\s+//;
}
- else {
- $cpu_type = 'UP';
+ # only elbrus shows L1 / L3 cache data in cpuinfo, cpu_sys data should show
+ # for newer full linux.
+ elsif ($counts->{'cpu-cores'} &&
+ ($tests->{'elbrus'} || $counts->{'cpu-cores'} > 1)) {
+ $counts->{'cores-multiplier'} = $counts->{'cpu-cores'};
+ }
+ # last test to catch some corner cases
+ # seen a case where a xeon vm in a dual xeon system actually had 2 cores, no MT
+ # so it reported 4 siblings, 2 cores, but actually only had 1 core per virtual cpu
+ # print "prc: $counts->{'processors'} phc: $counts->{'physical'} coc: $counts->{'cores'} cpc: $counts->{'cpu-cores'}\n";
+ # this test was for arm but I think it applies to all risc, but risc will be sys
+ if (!%risc &&
+ $counts->{'processors'} == $counts->{'physical'} * $counts->{'cores'} &&
+ $counts->{'cpu-cores'} > $counts->{'cores'}){
+ $tests->{'ht'} = 0;
+ # $tests->{'xeon'} = 0;
+ $tests->{'intel'} = 0;
+ $counts->{'cpu-cores'} = 1;
+ $counts->{'cores'} = 1;
+ $cpu->{'siblings'} = 1;
}
eval $end if $b_log;
- return $cpu_type;
}
# Legacy: this data should be comfing from the /sys tool now.
@@ -12436,143 +12735,9 @@ sub cp_elbrus_data {
eval $end if $b_log;
return $return;
}
+## END LEGACY CPU DATA ENGINE ##
-sub cp_speed_data {
- eval $start if $b_log;
- my ($cpu,$cpu_sys) = @_;
- my $info = {};
- if (defined $cpu_sys->{'data'}){
- if (defined $cpu_sys->{'data'}{'speeds'}{'min-freq'}){
- $cpu->{'min-freq'} = $cpu_sys->{'data'}{'speeds'}{'min-freq'};
- }
- if (defined $cpu_sys->{'data'}{'speeds'}{'max-freq'}){
- $cpu->{'max-freq'} = $cpu_sys->{'data'}{'speeds'}{'max-freq'};
- }
- if (defined $cpu_sys->{'data'}{'speeds'}{'scaling-min-freq'}){
- $cpu->{'scaling-min-freq'} = $cpu_sys->{'data'}{'speeds'}{'scaling-min-freq'};
- }
- if (defined $cpu_sys->{'data'}{'speeds'}{'scaling-max-freq'}){
- $cpu->{'scaling-max-freq'} = $cpu_sys->{'data'}{'speeds'}{'scaling-max-freq'};
- }
- # we don't need to see these if they are the same
- if ($cpu->{'min-freq'} && $cpu->{'max-freq'} &&
- $cpu->{'scaling-min-freq'} && $cpu->{'scaling-max-freq'} &&
- $cpu->{'min-freq'} eq $cpu->{'scaling-min-freq'} &&
- $cpu->{'max-freq'} eq $cpu->{'scaling-max-freq'}){
- undef $cpu->{'scaling-min-freq'};
- undef $cpu->{'scaling-max-freq'};
- }
- if (defined $cpu_sys->{'data'}{'speeds'}{'all'}){
- # only replace if we got actual speed values from cpufreq, or if no legacy
- # sourced processors data. Handles fake syz core speeds for counts.
- if ((grep {$_} @{$cpu_sys->{'data'}{'speeds'}{'all'}}) ||
- !@{$cpu->{'processors'}}){
- $cpu->{'processors'} = $cpu_sys->{'data'}{'speeds'}{'all'};
- }
- }
- if (defined $cpu_sys->{'data'}{'cpufreq-boost'}){
- $cpu->{'boost'} = $cpu_sys->{'data'}{'cpufreq-boost'};
- }
- }
- if (defined $cpu->{'processors'}){
- if (scalar @{$cpu->{'processors'}} > 1){
- my ($agg,$high) = (0,0);
- for (@{$cpu->{'processors'}}){
- next if !$_; # bsds might have 0 or undef value, that's junk
- $agg += $_;
- $high = $_ if $_ > $high;
- }
- if ($agg){
- $cpu->{'avg-freq'} = int($agg/scalar @{$cpu->{'processors'}});
- $cpu->{'cur-freq'} = $high;
- $info->{'avg-speed-key'} = 'avg';
- $info->{'speed'} = $cpu->{'avg-freq'};
- if ($high > $cpu->{'avg-freq'}){
- $cpu->{'high-freq'} = $high;
- $info->{'high-speed-key'} = 'high';
- }
- }
- }
- elsif ($cpu->{'processors'}[0]) {
- $cpu->{'cur-freq'} = $cpu->{'processors'}[0];
- $info->{'speed'} = $cpu->{'cur-freq'};
- }
- }
- # BSDs generally will have processors count, but not per core speeds
- if ($cpu->{'cur-freq'} && !$info->{'speed'}){
- $info->{'speed'} = $cpu->{'cur-freq'};
- }
- if ($cpu->{'min-freq'} || $cpu->{'max-freq'}){
- ($info->{'min-max'},$info->{'min-max-key'}) = cp_speed_min_max(
- $cpu->{'min-freq'},
- $cpu->{'max-freq'});
- }
- if ($cpu->{'scaling-min-freq'} || $cpu->{'scaling-max-freq'}){
- ($info->{'scaling-min-max'},$info->{'scaling-min-max-key'}) = cp_speed_min_max(
- $cpu->{'scaling-min-freq'},
- $cpu->{'scaling-max-freq'},
- 'sc');
- }
- if ($cpu->{'cur-freq'}){
- if ($show{'short'}){
- $info->{'speed-key'} = 'speed';
- }
- elsif ($show{'cpu-basic'}){
- $info->{'speed-key'} = 'speed (MHz)';
- }
- else {
- $info->{'speed-key'} = 'Speed (MHz)';
- }
- }
- eval $end if $b_log;
- return $info;
-}
-
-sub cp_speed_min_max {
- my ($min,$max,$type) = @_;
- my ($min_max,$key);
- if ($min && $max){
- $min_max = "$min/$max";
- $key = "min/max";
- }
- elsif ($max){
- $min_max = $max;
- $key = "max";
- }
- elsif ($min){
- $min_max = $min;
- $key = "min";
- }
- $key = $type . '-' . $key if $type && $key;
- return ($min_max,$key);
-}
-
-# args: 0: cpu, by ref; 1: update $tests by reference
-sub cp_test_types {
- my ($cpu,$tests) = @_;
- if ($cpu->{'type'} eq 'intel'){
- $$tests{'intel'} = 1;
- $$tests{'xeon'} = 1 if $cpu->{'model_name'} =~ /Xeon/i;
- }
- elsif ($cpu->{'type'} eq 'amd'){
- if ($cpu->{'family'} && $cpu->{'family'} eq '17'){
- $$tests{'amd-zen'} = 1;
- if ($cpu->{'model_name'}){
- if ($cpu->{'model_name'} =~ /Ryzen/i){
- $$tests{'ryzen'} = 1;
- }
- elsif ($cpu->{'model_name'} =~ /EPYC/i){
- $$tests{'epyc'} = 1;
- }
- }
- }
- }
- elsif ($cpu->{'type'} eq 'elbrus'){
- $$tests{'elbrus'} = 1;
- }
-}
-
-## CPU UTILITIES ##
+## CPU SHARED UTILITIES ##
# args: 0: vendor_id,like GenuineIntel, AuthenticAMD
sub cpu_vendor {
eval $start if $b_log;
@@ -12604,7 +12769,6 @@ sub set_cpu_data {
'bogomips' => 0,
'cores' => 0,
'cur-freq' => 0, # MHz
- 'dies' => 0,
'family' => '',
'flags' => '',
'ids' => [],
@@ -12650,6 +12814,7 @@ sub system_cpu_name {
eval $end if $b_log;
return $cpus;
}
+## END CPU SHARED UTILITIES ##
## CLEANERS/OUTPUT HANDLERS ##
# MHZ - cell cpus
@@ -12690,9 +12855,11 @@ sub hex_and_decimal {
}
return $data;
}
+## END CLEANERS/OUTPUT HANDLERS
}
+## END CpuItem ##
-## DriveItem
+## DriveItem ##
{
package DriveItem;
my ($b_hddtemp,$b_nvme,$smartctl_missing,$vendors);
@@ -13996,14 +14163,14 @@ sub set_disk_vendors {
['(\bINTEL\b|^(SSD(PAM|SA2)|HBR|(MEM|SSD)PEB?K|SSD(MCE|S[AC])))','\bINTEL\b','Intel',''],
['^(Intel[\s_-]?)?SRCSAS?','^Intel','Intel RAID',''],
# note: S[AV][1-9]\d can trigger false positives
- ['(K(ING)?STON|^(OM8P|RBU|S[AV][1234]00|S[HMN]S|SK[CY]|SQ5|SS200|SVP|SS0|SUV|SNV|T52|T[AB]29|Ultimate CF)|V100|DataTraveler|DT\s?(DUO|Microduo|101)|HyperX|13fe\b)','(KINGSTON|13fe)','Kingston',''], # maybe SHS: SHSS37A SKC SUV
+ ['(K(ING)?STON|^(A400|ASTC|OM8P|RBU|S100\d\d|S[AV][1234]00|S[HMN]S|SK[CY]|SQ5|SS200|SVP|SS0|SUV|SNV|T52|T[ABY]29|Y29\d|Ultimate CF)|V100|DataTraveler|DT\s?(DUO|Microduo|101)|HyperX|13fe\b)','(KINGSTON|13fe)','Kingston',''], # maybe SHS: SHSS37A SKC SUV
# must come before samsung MU. NOTE: toshiba can have: TOSHIBA_MK6475GSX: mush: MKNSSDCR120GB_
['(^MKN|Mushkin)','Mushkin','Mushkin',''], # MKNS
# MU = Multiple_Flash_Reader too risky: |M[UZ][^L] HD103SI HD start risky
# HM320II HM320II HM
- ['(SAMSUNG|^(AWMB|[BC]DS20|[BC]WB|BJ[NT]|[BC]GND|CJ[NT]|CKT|CUT|[DG]3 Station|DUO\b|DUT|EB\dMW|E[CS]\d[A-Z]\d|FD\d[A-Z]\dGE4S5|[GS]2 Portable|GN|HD\d{3}[A-Z]{2}$|(HM|SP)\d{2}|HS\d|M[AB]G\d[FG]|MCC|MCBOE|MCG\d+GC|[CD]JN|MZ|^G[CD][1-9][QS]|P[BM]\d|(SSD\s?)?SM\s?841)|^SSD\s?[89]\d{2}\s(DCT|PRO|QVD|\d+[GT]B)|\bEVO\b|SV\d|[BE][A-Z][1-9]QT|YP\b|[CH]N-M|MMC[QR]E)','SAMSUNG','Samsung',''], # maybe ^SM, ^HM
+ ['(SAMSUNG|^(AGN[BD]|AWMB|[BC]DS20|[BCD]WB|BJ[NT]|[BC]GND|CJ[NT]|CKT|CUT|[DG]3 Station|DUO\b|DUT|EB\dMW|E[CS]\d[A-Z]\d|ED2|EE4|FD\d[A-Z]\d|[GS]2 Portable|GE4|GN|HD\d{3}[A-Z]{2}$|(HM|SP)\d{2}|HS\d|KLUD|M[AB]G\d[FG]|MCC|MCBOE|MCG\d+GC|[CD]JN|MZ|^G[CD][1-9][QS]|P[BM]\d|(SSD\s?)?SM\s?841)|^SSD\s?[89]\d{2}\s(DCT|PRO|QVD|\d+[GT]B)|\bEVO\b|SV\d|[BE][A-Z][1-9]QT|YP\b|[CH]N-M|MMC[QR]E)','SAMSUNG','Samsung',''], # maybe ^SM, ^HM
# Android UMS Composite?U1
- ['(SanDisk|0781|^(A[BCD]LC[DE]|AFGCE|D[AB]4|DX[1-9]|Extreme|Firebird|S[CD]\d{2}G|SC\d{3,4}|SD(CF|S[S]?[ADQ]|SL\d+G|SU\d|U\d|\sUltra)|SDW[1-9]|SE\d{2}|SEM\d{2}|\d[STU]|U(3\b|1\d0))|Clip Sport|Cruzer|iXpand|SN(\d+G|128|256)|SSD (Plus|U1[01]0) [1-9]|ULTRA\s(FIT|trek|II)|^X[1-6]\d{2})','(SanDisk|0781)','SanDisk',''],
+ ['(SanDisk|0781|^(A[BCD]LC[DE]|AFGCE|D[AB]4|DX[1-9]|Extreme|EZSD|Firebird|S[CD]\d{2}G|SB\d+G|SC\d{3,4}|SD(CF|S[S]?[ADQ]|SL\d+G|SU\d|U\d|\sUltra)|SDW[1-9]|SE\d{2}|SEM\d{2}|\d[STU]|U(3\b|1\d0))|Clip Sport|Cruzer|iXpand|SN(\d+G|128|256)|SSD (Plus|U1[01]0) [1-9]|SU(02|04|08|16|32|64)G|ULTRA\s(FIT|trek|II)|^X[1-6]\d{2})','(SanDisk|0781)','SanDisk',''],
# these are HP/Sandisk cobranded. DX110064A5xnNMRI ids as HP and Sandisc
['(^DX[1-9])','^(HP\b|SANDDISK)','Sandisk/HP',''], # ssd drive, must come before seagate ST test
# real, SSEAGATE Backup+; XP1600HE30002 | 024 HN (spinpoint) ; possible usb: 24AS
@@ -14032,7 +14199,7 @@ sub set_disk_vendors {
['^((ATA\s)?Hitachi|HCS|HD[PST]|DK\d|IC|(HDD\s)?HT|HU|HMS|HDE|0G\d|IHAT)','Hitachi','Hitachi',''],
# vb: VB0250EAVER but clashes with vbox; HP_SSD_S700_120G ;GB0500EAFYL GB starter too generic?
['^(HP\b|c350|DF\d|EG0\d{3}|EX9\d\d|G[BJ]\d|F[BK]|0-9]|HC[CPY]\d|MM\d{4}|[MV]B[0-6]|PSS|VO0|VK0|v\d{3}[bgorw]$|x\d{3}[w]$|XR\d{4})','^HP','HP',''],
- ['^(Lexar|LSD|JumpDrive|JD\s?Firefly|LX\d|WorkFlow)','^Lexar','Lexar',''], # mmc-LEXAR_0xb016546c; JD Firefly;
+ ['^(Lexar|LSD|JumpDrive|JD\s?Firefly|LX\d|NCard|ND\d+GB|WorkFlow)','^Lexar','Lexar',''], # mmc-LEXAR_0xb016546c; JD Firefly;
# these must come before maxtor because STM
['^STmagic','^STmagic','STmagic',''],
['^(STMicro|SMI|CBA)','^(STMicroelectronics|SMI)','SMI (STMicroelectronics)',''],
@@ -14054,6 +14221,7 @@ sub set_disk_vendors {
['^2[\s-]?Power','^2[\s-]?Power','2-Power',''],
['^(3ware|9650SE)','^3ware','3ware (controller)',''],
['^5ACE','^5ACE','5ACE',''], # could be seagate: ST316021 5ACE
+ ['^51RISC','^51RISC','51risc',''],
['^(Aar(vex)?|AX\d{2})','^AARVEX','AARVEX',''],
['^(AbonMax|ASU\d)','^AbonMax','AbonMax',''],
['^Acasis','^Acasis','Acasis (hub)',''],
@@ -14071,6 +14239,7 @@ sub set_disk_vendors {
['^Aigo','^Aigo','Aigo',''],
['^AirDisk','^AirDisk','AirDisk',''],
['^Aireye','^Aireye','Aireye',''],
+ ['^AiteFeir','^AiteFeir','AiteFeir',''],
['^Alcatel','^Alcatel','Alcatel',''],
['^(Alcor(\s?Micro)?|058F)','^(Alcor(\s?Micro)?|058F)','Alcor Micro',''],
['^Alfawise','^Alfawise','Alfawise',''],
@@ -14080,6 +14249,8 @@ sub set_disk_vendors {
['^ANK','^Anker','Anker',''],
['^Ant[\s_-]?Esports','^Ant[\s_-]?Esports','Ant Esports',''],
['^Anucell','^Anucell','Anucell',''],
+ ['^Aoluska','^Aoluska','Aoluska',''],
+ ['^(Aotec|AOK)','^Aotec','Aotec',''],
['^Apotop','^Apotop','Apotop',''],
# must come before AP|Apacer
['^(APPLE|iPod|SSD\sSM\d+[CEGT])','^APPLE','Apple',''],
@@ -14087,6 +14258,7 @@ sub set_disk_vendors {
['^(Apricom|SATAWire)','^Apricom','Apricom',''],
['^(A-?RAM|ARSSD)','^A-?RAM','A-RAM',''],
['^Arch','^Arch(\s*Memory)?','Arch Memory',''],
+ ['(Ardor|\bAlly\b|\bAL\d\d)','Ardor(\sGaming)?','Ardor Gaming',''],
['^(Asenno|AS[1-9])','^Asenno','Asenno',''],
['^Asgard','^Asgard','Asgard',''],
['^ASint','^ASint','ASint',''],
@@ -14108,6 +14280,8 @@ sub set_disk_vendors {
['^BIWIN','^BIWIN','BIWIN',''],
['^Blackpcs','^Blackpcs','Blackpcs',''],
['^(BlitzWolf|BW-?PSSD)','^BlitzWolf','BlitzWolf',''],
+ ['^(BlueCase|BS2N\d)','^BlueCase[\s-]?(Horizon)?','BlueCase Horizon',''],
+ ['^(Blue[\s-]?Feather|BF\d)','^Blue[\s-]?Feather','Blue Feather',''],
['^(BlueRay|SDM\d)','^BlueRay','BlueRay',''],
['^Bory','^Bory','Bory',''],
['^Braveeagle','^Braveeagle','BraveEagle',''],
@@ -14122,7 +14296,9 @@ sub set_disk_vendors {
['^CHIPAL','^CHIPAL','CHIPAL',''],
['^(Chipsbank|CHIPSBNK)','^Chipsbank','Chipsbank',''],
['^(Chipfancie)','^Chipfancier','Chipfancier',''],
+ ['\bCKS','\bCKS','CKS',''],
['^Clover','^Clover','Clover',''],
+ ['^Codeo','^Codeo','Codeo',''],
['^CODi','^CODi','CODi',''],
['^Colorful\b','^Colorful','Colorful',''],
['^CONSISTENT','^CONSISTENT','Consistent',''],
@@ -14148,8 +14324,9 @@ sub set_disk_vendors {
['^DGM','^DGM\b','DGM',''],
['^(DICOM|MAESTRO)','^DICOM','DICOM',''],
['^Digifast','^Digifast','Digifast',''],
+ ['^(DIGIRICH|DGSSD)','^DIGIRICH','DIGIRICH',''],
['^DIGITAL\s?FILM','DIGITAL\s?FILM','Digital Film',''],
- ['^(Digma|Run(\sY2)?\b)','^Digma','Digma',''],
+ ['(Digma|\bRun\s)','\bDigma','Digma',''],
['^Dikom','^Dikom','Dikom',''],
['^DINGGE','^DINGGE','DINGGE',''],
['^Disain','^Disain','Disain',''],
@@ -14160,12 +14337,13 @@ sub set_disk_vendors {
['^(Dogfish|M\.2 2242|Shark)','^Dogfish(\s*Technology)?','Dogfish Technology',''],
['^DragonDiamond','^DragonDiamond','DragonDiamond',''],
['^(DREVO\b|X1\s\d+[GT])','^DREVO','Drevo',''],
+ ['^Drobo','^Drobo','Drobo',''],
['^DSS','^DSS DAHUA','DSS DAHUA',''],
['^(Duex|DX\b)','^Duex','Duex',''], # DX\d may be starter for sandisk string
['^(Dynabook|AE[1-3]00)','^Dynabook','Dynabook',''],
# DX1100 is probably sandisk, but could be HP, or it could be hp branded sandisk
['^(Eaget|V8$)','^Eaget','Eaget',''],
- ['^(Easy[\s-]?Memory)','^Easy[\s-]?Memory','Easy Memory',''],
+ ['^(Easy[\s-]?Memory|EYM\d)','^Easy[\s-]?Memory','Easy Memory',''],
['^EDGE','^EDGE','EDGE Tech',''],
['^(EDILOCA|ES\d+\b)','^EDILOCA','Ediloca',''],
['^Elecom','^Elecom','Elecom',''],
@@ -14180,8 +14358,10 @@ sub set_disk_vendors {
['^(Shenzhen\s)?Etopso(\sTechnology)?','^(Shenzhen\s)?Etopso(\sTechnology)?','Etopso',''],
['^EURS','^EURS','EURS',''],
['^eVAULT','^eVAULT','eVAULT',''],
- ['^EVM','^EVM','EVM',''],
+ ['\bEVM','\bEVM','EVM',''],
['^eVtran','^eVtran','eVtran',''],
+ ['\bExbom','^\bExbom','Exbom',''],
+ ['^(ExeGate|EX\d\d)','^ExeGate','ExeGate',''],
# NOTE: ESA3... may be IBM PCIe SAD card/drives
['^(EXCELSTOR|r technology)','^EXCELSTOR( TECHNO(LOGY)?)?','ExcelStor',''],
['^EXRAM','^EXRAM','EXRAM',''],
@@ -14196,15 +14376,18 @@ sub set_disk_vendors {
['^FiiO','^FiiO','FiiO',''],
['^FixMeStick','^FixMeStick','FixMeStick',''],
['^(FIKWOT|FS\d{3})','^FIKWOT','Kikwot',''],
+ ['^FNK[\s-]?TECH','^FNK[\s-]?TECH','FNK Tech',''],
['^Fordisk','^Fordisk','Fordisk',''],
# FK0032CAAZP/FB160C4081 FK or FV can be HP but can be other things
['^(FORESEE|B[123]0)|P900F|S900M','^FORESEE','Foresee',''],
['^Founder','^Founder','Founder',''],
+ ['^(FOXIN|FX\d\d)','^FOXIN','FOXIN',''],
['^(FOXLINE|FLD)','^FOXLINE','Foxline',''], # russian vendor?
['^(Gateway|W800S)','^Gateway','Gateway',''],
['^Freecom','^Freecom(\sFreecom)?','Freecom',''],
['^(FronTech)','^FronTech','Frontech',''],
['^(Fuhler|FL-D\d{3})','^Fuhler','Fuhler',''],
+ ['^(FuturePath|FPT)','^FuturePath([\s-]?Technologies)?','FuturePath',''],
['^Gaiver','^Gaiver','Gaiver',''],
['^(GALAX\b|Gamer\s?L|TA\dD|Gamer[\s-]?V)','^GALAX','GALAX',''],
['^Galaxy\b','^Galaxy','Galaxy',''],
@@ -14212,14 +14395,17 @@ sub set_disk_vendors {
['^(Garmin|Fenix|Nuvi|Zumo)','^Garmin','Garmin',''],
['^Geil','^Geil','Geil',''],
['^GelL','^GelL','GelL',''], # typo for Geil? GelL ZENITH R3 120GB
- ['^(Generic|A3A|G1J3|M0S00|SCA\d{2}|SCY|SLD|S0J\d|UY[567])','^Generic','Generic',''],
+ ['^(GemiBook|G52)','^GemiBook','GemiBook',''],
+ ['^(Generic|58A4|58K7|A3A|G1J3|M0S00|SCA\d{2}|SCY|SLD|S0J\d|UY[567])','^Generic','Generic',''],
['^(Genesis(\s?Logic)?|05e3)','(Genesis(\s?Logic)?|05e3)','Genesis Logic',''],
['^Geonix','^Geonix','Geonix',''],
+ ['^(Gerffins)','^Gerffins','Gerffins',''],
['^Getrich','^Getrich','Getrich',''],
['^(Gigabyte|GP-G)','^Gigabyte','Gigabyte',''], # SSD
['^Gigastone','^Gigastone','Gigastone',''],
['^Gigaware','^Gigaware','Gigaware',''],
['^GJN','^GJN\b','GJN',''],
+ ['^(Global[\s-]?Memory)','Global[\s-]?Memory','Global Memory',''],
['^(Gloway|FER\d)','^Gloway','Gloway',''],
['^GLOWY','^GLOWY','Glowy',''],
['^Goldendisk','^Goldendisk','Goldendisk',''],
@@ -14230,13 +14416,15 @@ sub set_disk_vendors {
['^(Goline)','^Goline','Goline',''],
# Wilk Elektronik SA, poland
['^((Wilk|WE)\s*)?(GOODRAM|GOODDRIVE|IR[\s-]?SSD|IRP|SSDPR|Iridium)','^GOODRAM','GOODRAM',''],
+ ['^(Gost)','^Gost','Gost',''],
['^(GreatWall|GW\d{3})','^GreatWall','GreatWall',''],
['^(GreenHouse|GH\b)','^GreenHouse','GreenHouse',''],
['^Gritronix','^Gritronixx?','Gritronix',''],
# supertalent also has FM: |FM
['^(G[\.]?SKILL)','^G[\.]?SKILL','G.SKILL',''],
['^G[\s-]*Tech','^G[\s-]*Tech(nology)?','G-Technology',''],
- ['^(Gudga|GIM\d+|G[NV](R\d|\d{2,4}\b))','^Gudga','Gudga',''],
+ ['\b(GTL|XEON)','\bGTL\b','GTL',''],
+ ['^(Gudga|GIM\d+|G[NV](R\d|\d{2,4}\b)|GS-?\d\d)','^Gudga','Gudga',''],
['^(Hajaan|HS[1-9])','^Haajan','Haajan',''],
['^Haizhide','^Haizhide','Haizhide',''],
['^(Hama|FlashPen\s?Fancy)','^Hama','Hama',''],
@@ -14245,20 +14433,24 @@ sub set_disk_vendors {
['^Hectron','^Hectron','Hectron',''],
['^HEMA','^HEMA','HEMA',''],
['(HEORIADY|^HX-0)','^HEORIADY','HEORIADY',''],
+ ['^(Heovose)','^Heovose','Heovose',''],
['^(Hikvision|HKVSN|HS-SSD)','^Hikvision','Hikvision',''],
['^Hi[\s-]?Level ','^Hi[\s-]?Level ','Hi-Level',''], # ^HI\b with no Level?
['^(Hisense|H8G)','^Hisense','Hisense',''],
+ ['^(HJDK|MHD)','^HJDK','HJDK',''],
+ ['^(HMZM)','^HMZM','HMZM',''],
['^Hoodisk','^Hoodisk','Hoodisk',''],
+ ['^(HRUIYL)','^HRUIYL','HRUIYL',''],
['^(HUAWEI|HWE)','^HUAWEI','Huawei',''],
['^Hypertec','^Hypertec','Hypertec',''],
['^HyperX','^HyperX','HyperX',''],
['^(HYSSD|HY-)','^HYSSD','HYSSD',''],
['^(Hyundai|C2S\d|Sapphire)','^Hyundai','Hyundai',''],
['^iMRAM','^iMRAM','iMRA',''],
- ['^(IBM|DT|ESA[1-9]|ServeRaid)','^IBM','IBM',''], # M5110 too common
+ ['^(IBM|DT|ESA[1-9]|GBR|ServeRaid)','^IBM','IBM',''], # M5110 too common
['^IEI Tech','^IEI Tech(\.|nology)?( Corp(\.|oration)?)?','IEI Technology',''],
['^(IGEL|UD Pocket)','^IGEL','IGEL',''],
- ['^(Imation|Nano\s?Pro|HQT)','^Imation(\sImation)?','Imation',''], # Imation_ImationFlashDrive; TF20 is imation/tdk
+ ['^(Imation|Nano\s?Pro|HQT|Ridge)','^Imation(\sImation)?','Imation',''], # Imation_ImationFlashDrive; TF20 is imation/tdk
['^(IMC|Kanguru)','^IMC\b','IMC',''],
['^(Inateck|FE20)','^Inateck','Inateck',''],
['^(Inca\b|Npenterprise)','^Inca','Inca',''],
@@ -14280,6 +14472,7 @@ sub set_disk_vendors {
['^(INO-|i\.?norys)','^i\.?norys','i.norys',''],
['^(Integrated[\s-]?Technology|IT\d+)','^Integrated[\s-]?Technology','Integrated Technology',''],
['^(Iomega|ZIP\b|Clik!)','^Iomega','Iomega',''],
+ ['^(IPASON|PD\d\d)','^IPASON','IPASON',''],
['^(i[\s_-]?portable\b|ATCS)','^i[\s_-]?portable','i-Portable',''],
['^ISOCOM','^ISOCOM','ISOCOM (Shenzhen Longsys Electronics)',''],
['^iTE[\s-]*Tech','^iTE[\s-]*Tech(nology)?','iTE Tech',''],
@@ -14289,9 +14482,13 @@ sub set_disk_vendors {
['^Jingyi','^Jingyi','Jingyi',''],
# NOTE: ITY2 120GB hard to find
['^JMicron','^JMicron(\s?Tech(nology)?)?','JMicron Tech',''], #JMicron H/W raid
+ ['^Joint','^Joint','Joint',''],
['^JSYERA','^JSYERA','Jsyera',''],
['^(Jual|RX7)','^Jual','Jual',''],
['^(J\.?ZAO|JZ)','^J\.?ZAO','J.ZAO',''],
+ ['^(KAPBOM|KA-)','KAPBOM','KAPBOM',''],
+ ['^(KaBuM!?|KBM)','KaBuM!?','KaBuM!',''],
+ ['(Kaizen|KZ\d\d)','Kaizen','Kaizen',''],
['^Kazuk','^Kazuk','Kazuk',''],
['(\bKDI\b|^OM3P)','\bKDI\b','KDI',''],
['^KEEPDATA','^KEEPDATA','KeepData',''],
@@ -14303,19 +14500,21 @@ sub set_disk_vendors {
['^Kingchux[\s-]?ing','^Kingchux[\s-]?ing','Kingchuxing',''],
['^(KINGCOMP|KCSSD)','^KINGCOMP','KingComp',''],
['(KingDian|^NGF|S(280|400))','KingDian','KingDian',''],
- ['^(Kingfast|TYFS)','^Kingfast','Kingfast',''],
+ ['^(Kingfast|TYFS|2710)','^Kingfast','Kingfast',''],
['^KingMAX','^KingMAX','KingMAX',''],
['^Kingrich','^Kingrich','Kingrich',''],
['^Kingsand','^Kingsand','Kingsand',''],
['KING\s?SHA\s?RE','KING\s?SHA\s?RE','KingShare',''],
- ['^(KingSpec|ACSC|C3000|KS[DQ]|MSH|N[ET]-\d|NX-\d{2,4}|P3$|P4\b|PA[_-]?(18|25)|Q-180|SPK|T-(3260|64|128)|Z(\d\s|F\d))','^KingSpec','KingSpec',''],
+ ['^(KingSpec|ACSC|C3000|CHA|KS[DQ]|MSH|N[ET]-\d|NX-\d{2,4}|P3$|P4\b|PA[_-]?(18|25)|Q-180|SPK|T-(3260|64|128)|Z(\d\s|F\d))','^KingSpec','KingSpec',''],
['^KingSSD','^KingSSD','KingSSD',''],
# kingwin docking, not actual drive
['^(EZD|EZ-Dock)','','Kingwin Docking Station',''],
['^Kingwin','^Kingwin','Kingwin',''],
['^KLLISRE','^KLLISRE','KLLISRE',''],
- ['(KIOXIA|^K[BX]G\d)','KIOXIA','KIOXIA',''], # company name comes after product ID
+ # company name comes after product ID
+ ['(KIOXIA|\bCL\d-|^K[BX]G\d|SSSTC|Solid\s?State\s?Storage\s?Tech)','KIOXIA','KIOXIA',''],
['^(KLEVV|NEO\sN|CRAS)','^KLEVV','KLEVV',''],
+ ['^(KNUP|KP\b)','^KNUP','KNUP',''],
['^(Kodak|Memory\s?Saver)','^Kodak','Kodak',''],
['^(KOOTION)','^KOOTION','KOOTION',''],
['^(KUAIKAI|MSAM)','^KUAIKAI','KuaKai',''],
@@ -14329,6 +14528,7 @@ sub set_disk_vendors {
['^Lapcare','^Lapcare','Lapcare',''],
['^(Lazos|L-?ISS)','^Lazos','Lazos',''],
['^LDLC','^LDLC','LDLC',''],
+ ['^(Leica|MSD\d\d)','^Leica','Leica',''],
# LENSE30512GMSP34MEAT3TA / UMIS RPITJ256PED2MWX
['^(LEN|UMIS|Think)','^Lenovo','Lenovo',''],
['^RPFT','','Lenovo O.E.M.',''],
@@ -14349,6 +14549,8 @@ sub set_disk_vendors {
['^(Mach\s*Xtreme|MXSSD|MXU|MX[\s-])','^Mach\s*Xtreme','Mach Xtreme',''],
['^(MacroVIP|MV(\d|GLD))','^MacroVIP','MacroVIP',''], # maybe MV alone
['^Mainic','^Mainic','Mainic',''],
+ ['^(Mancer|MCR)','^ Mancer[\s-]?Reaper','Mancer Reaper',''],
+ ['^MARKVISION','^MARKVISION','MarkVision',''],
['^(MARSHAL\b|MAL\d)','^MARSHAL','Marshal',''],
['^Maxell','^Maxell','Maxell',''],
['^Maximus','^Maximus','Maximus',''],
@@ -14357,19 +14559,23 @@ sub set_disk_vendors {
['^Maxone','^Maxone','Maxone',''],
['^MARVELL','^MARVELL','Marvell',''],
['^Maxsun','^Maxsun','Maxsun',''],
+ ['^(McQuest)','^McQuest([\s-]?Digital)?','McQuest Digital',''],
['^MDT\b','^MDT','MDT (rebuilt WD/Seagate)',''], # mdt rebuilds wd/seagate hdd
# MD1TBLSSHD, careful with this MD starter!!
['^MD[1-9]','^Max\s*Digital','MaxDigital',''],
+ ['^(Media[\s-]?Dice|MD[\s-])','^Media[\s-]?Dice','MediaDice',''],
['^Medion','^Medion','Medion',''],
['^(MEDIAMAX|WL\d{2})','^MEDIAMAX','MediaMax',''],
['^(Memorex|TravelDrive|TD\s?Classic)','^Memorex','Memorex',''],
['^Mengmi','^Mengmi','Mengmi',''],
- ['^MicroFrom','^MicroFrom','MicroFrom',''],
['^MGTEC','^MGTEC','MGTEC',''],
+ ['^MicroFrom','^MicroFrom','MicroFrom',''],
+ ['^(MILLENNIUM[\s-]?TECHNOLOGY|MIL\d\d)','^MILLENNIUM[\s-]?TECHNOLOGY','Millenium Technology',''],
# must come before micron
['^(Mtron|MSP)','^Mtron','Mtron',''],
# note: C300/400 can be either micron or crucial, but C400 is M4 from crucial
- ['(^(Micron|2200[SV]|MT|M5|(\d+|[CM]\d+)\sMTF)|00-MT)','^Micron','Micron',''],# C400-MTFDDAK128MAM
+ # micron can be in middle of model name
+ ['(\bMicron|^(2200[SV]|MT|M5|(\d+|[CM]\d+)\sMTF)|00-MT)','\bMicron','Micron',''],# C400-MTFDDAK128MAM
['^(Microsoft|S31)','^Microsoft','Microsoft',''],
['^MidasForce','^MidasForce','MidasForce',''],
['^Milan','^Milan','Milan',''],
@@ -14396,7 +14602,7 @@ sub set_disk_vendors {
['^(Myson)','^Myson([\s-]?Century)?([\s-]?Inc\.?)?','Myson Century',''],
['^(Natusun|i-flashdisk)','^Natusun','Natusun',''],
['^(Neo\s*Forza|NFS\d)','^Neo\s*Forza','Neo Forza',''],
- ['^(Netac|NS\d{3}|OnlyDisk|S535N)','^Netac','Netac',''],
+ ['^(Netac|NS\d{3}|OnlyDisk|S535N|SMTC)','^Netac','Netac',''],
['^Newsmy','^Newsmy','Newsmy',''],
['^NFHK','^NFHK','NFHK',''],
# NGFF is a type, like msata, sata
@@ -14407,6 +14613,7 @@ sub set_disk_vendors {
['^ODYS','^ODYS','ODYS',''],
['^Olympus','^Olympus','Olympus',''],
['^Orico','^Orico','Orico',''],
+ ['(Origin|Inception|^TLC\d)','^Origin','Origin',''],
['^Ortial','^Ortial','Ortial',''],
['^OSC','^OSC\b','OSC',''],
['^(Ovation)','^Ovation','Ovation',''],
@@ -14416,8 +14623,9 @@ sub set_disk_vendors {
['^(Parker|TP00)','^Parker','Parker',''],
['^(Pasoul|OASD)','^Pasoul','Pasoul',''],
['^(Patriot|PS[8F]|P2\d{2}|PBT|VPN|Viper|Burst|Blast|Blaze|Pyro|Ignite)','^Patriot([-\s]?Memory)?','Patriot',''],#Viper M.2 VPN100
- ['^PERC\b','','Dell PowerEdge RAID Card',''], # ssd
- ['(PHISON[\s-]?|ESR\d|PSE)','PHISON[\s-]?','Phison',''],# E12-256G-PHISON-SSD-B3-BB1
+ ['^PERC\b','','Dell PowerEdge RAID Card',''], # ssd
+ ['(\bPhilips)','\bPhilips','Philips',''],
+ ['(PHISON[\s-]?|ESR\d|PS[5E]|311CD|\bSSG\d\d)','PHISON[\s-]?','Phison',''],# E12-256G-PHISON-SSD-B3-BB1
['^(Pichau[\s-]?Gaming|PG\d{2})','^Pichau[\s-]?Gaming','Pichau Gaming',''],
['^Pioneer','Pioneer','Pioneer',''],
['^Platinet','Platinet','Platinet',''],
@@ -14430,7 +14638,9 @@ sub set_disk_vendors {
# PS3109S9 is the result of an error condition with ssd controller: Phison PS3109
['^PUSKILL','^PUSKILL','Puskill',''],
['QEMU','^\d*QEMU( QEMU)?','QEMU',''], # 0QUEMU QEMU HARDDISK
+ ['^QNIX','^QNIX','QNIX',''],
['(^Quantum|Fireball)','^Quantum','Quantum',''],
+ ['(^Quanxing)','^Quanxing','Quanxing',''],
['(^QOOTEC|QMT)','^QOOTEC','QOOTEC',''],
['^(QUMO|Q\dDT)','^QUMO','Qumo',''],
['^QOPP','^QOPP','Qopp',''],
@@ -14442,6 +14652,7 @@ sub set_disk_vendors {
['^(Ramsta|R[1-9])','^Ramsta','Ramsta',''],
['^RCESSD','^RCESSD','RCESSD',''],
['^(Realtek|RTL)','^Realtek','Realtek',''],
+ ['^(Redragon|\bHaste)','^Redragon','Redragon',''],
['^(Reletech)','^Reletech','Reletech',''], # id: P400 but that's too short
['^RENICE','^RENICE','Renice',''],
['^RevuAhn','^RevuAhn','RevuAhn',''],
@@ -14451,6 +14662,7 @@ sub set_disk_vendors {
#RTDMA008RAV2BWL comes with lenovo but don't know brand
['^Runcore','^Runcore','Runcore',''],
['^Rundisk','^Rundisk','RunDisk',''],
+ ['^(RUNENG)','^RUNENG','RUNENG',''],
['^RZX','^RZX\b','RZX',''],
['^(S3Plus|S3\s?SSD)','^S3Plus','S3Plus',''],
['^(Sabrent|Rocket)','^Sabrent','Sabrent',''],
@@ -14463,6 +14675,8 @@ sub set_disk_vendors {
# SATAFIRM is an ssd failure message
['^SCUDA','^SCUDA','SCUDA',''],
['^(Sea\s?Tech|Transformer)','^Sea\s?Tech','Sea Tech',''],
+ ['^(SEIWHALE)','^SEIWHALE','SEIWHALE',''],
+ ['^(SIEMENS)','^SIEMENS','Siemens',''],
['^SigmaTel','^SigmaTel','SigmaTel',''],
# DIAMOND_040_GB
['^(SILICON\s?MOTION|SM\d|090c)','^(SILICON\s?MOTION|090c)','Silicon Motion',''],
@@ -14483,6 +14697,7 @@ sub set_disk_vendors {
['^Solidata','^Solidata','Solidata',''],
['^(SOLIDIGM|SSDPFK)','^SOLIDIGM\b','solidgm',''],
['^(Sony|IM9|Microvalut|S[FR]-)','^Sony','Sony',''],
+ # Note: SSC can be prefix for several companies
['^SSK\b','^SSK','SSK',''],
['^(SSSTC|CL1-)','^SSSTC','SSSTC',''],
['^(SST|SG[AN])','^SST\b','SST',''],
@@ -14491,6 +14706,7 @@ sub set_disk_vendors {
['\dSUN\d','^SUN(\sMicrosystems)?','Sun Microsystems',''],
['^Sundisk','^Sundisk','Sundisk',''],
['^SUNEAST','^SUNEAST','SunEast',''],
+ ['^Suntrsi','^Suntrsi','Suntrsi',''],
['^SuperMicro','^SuperMicro','SuperMicro',''],
['^Supersonic','^Supersonic','Supersonic',''],
['^SuperSSpeed','^SuperSSpeed','SuperSSpeed',''],
@@ -14506,7 +14722,7 @@ sub set_disk_vendors {
['^TANDBERG','^TANDBERG','Tanberg',''],
['^(TC[\s-]*SUNBOW|X3\s\d+[GT])','^TC[\s-]*SUNBOW','TCSunBow',''],
['^(TDK|TF[1-9]\d|LoR)','^TDK','TDK',''],
- ['^TEAC','^TEAC','TEAC',''],
+ ['(^TEAC|\bUF00)','^TEAC','TEAC',''],
['^(TEAM|T[\s-]?Create|CX[12]\b|L\d\s?Lite|T\d{3,}[A-Z]|TM\d|(Dark\s?)?L3\b|T[\s-]?Force)','^TEAM(\s*Group)?','TeamGroup',''],
['^(Teclast|CoolFlash)','^Teclast','Teclast',''],
['^(tecmiyo)','^tecmiyo','TECMIYO',''],
@@ -14537,6 +14753,7 @@ sub set_disk_vendors {
['^USBTech','^USBTech','USBTech',''],
['^(UNIC2)','^UNIC2','UNIC2',''],
['^(UG|Unigen)','^Unigen','Unigen',''],
+ ['^UnionSine','UnionSine','UnionSine',''],
['^(UNIREX)','^UNIREX','UNIREX',''],
['^(UNITEK)','^UNITEK','UNITEK',''],
['^(USBest|UT16)','^USBest','USBest',''],
@@ -14545,6 +14762,7 @@ sub set_disk_vendors {
['^(Value\s?Tech|VTP\d)','^Value\s?Tech','ValueTech',''],
['^VBOX','','VirtualBox',''],
['^(Veno|Scorp)','^Veno','Veno',''],
+ ['^(VenomRX|VRX)','^VenomRX','VenomRX',''],
['^(Verbatim|STORE\s?\'?N\'?\s?(FLIP|GO)|Vi[1-9]|OTG\s?Tiny)','^Verbatim','Verbatim',''],
['^V-?GEN','^V-?GEN','V-Gen',''],
['^VICK','VICK','VICK',''],
@@ -14559,10 +14777,12 @@ sub set_disk_vendors {
['^Walram','^Walram','WALRAM',''],
['^Walton','^Walton','Walton',''],
['^(Wearable|Air-?Stash)','^Wearable','Wearable',''],
+ ['\b(WiebeTech|eRazer)','WiebeTech','WiebeTech',''],
['^Wellcomm','^Wellcomm','Wellcomm',''],
+ ['^(WHALEKOM|WK)','^WHALEKOM','Whalekom',''],
['^(wicgtyp|[MN][V]?900)','^wicgtyp','wicgtyp',''],
['^Wilk','^Wilk','Wilk',''],
- ['^(WinMemory|SWG\d)','^WinMemory','WinMemory',''],
+ ['^(WinMemory|SW[GR]\d)','^WinMemory','WinMemory',''],
['^(Winton|WT\d{2})','^Winton','Winton',''],
['^(WISE)','^WISE','WISE',''],
['^WPC','^WPC','WPC',''], # WPC-240GB
@@ -14578,6 +14798,7 @@ sub set_disk_vendors {
['^XUNZHE','^XUNZHE','XUNZHE',''],
['^(Yangtze|ZhiTai|PC00[5-9]|SC00[1-9])','^Yangtze(\s*Memory)?','Yangtze Memory',''],
['^(Yeyian|valk)','^Yeyian','Yeyian',''],
+ ['^(YHJC|YHS)','^YHJC','YHJC',''],
['^(YingChu|YGC)','^YingChu','YingChu',''],
['^YongzhenWeiye','^YongzhenWeiye','YongzhenWeiye',''],
['^(YUCUN|R880)','^YUCUN','YUCUN',''],
@@ -14593,6 +14814,7 @@ sub set_disk_vendors {
['^(Zotac|ZTSSD)','^Zotac','Zotac',''],
['^ZOZT','^ZOZT','ZOZT',''],
['^ZSPEED','^ZSPEED','ZSpeed',''],
+ ['^Zsuit','^Zsuit','Zsuit',''],
['^ZTC','^ZTC','ZTC',''],
['^ZTE','^ZTE','ZTE',''],
['^(ZY|ZhanYao)','^ZhanYao([\s-]?data)','ZhanYao',''],
@@ -14844,7 +15066,7 @@ sub drive_speed {
}
}
-## GraphicItem
+## GraphicItem ##
{
package GraphicItem;
my ($b_primary,$b_wayland_data,%graphics,%mesa_drivers,
@@ -14915,7 +15137,7 @@ sub device_output {
$device = ($device) ? main::clean_pci($device,'output') : 'N/A';
# have seen absurdly verbose card descriptions, with non related data etc
if (length($device) > 85 || $size{'max-cols'} < 110){
- $device = main::filter_pci_long($device);
+ main::filter_pci_long(\$device);
}
push(@$rows, {
main::key($num++,1,1,'Device') => $device,
@@ -18068,7 +18290,7 @@ sub set_intel_data {
},
{'arch' => 'Gen-4',
'ids' => '2982|2983|2992|2993|29a2|29a3|29b2|29b3|29c2|29c3|29d2|29d3|2a02|' .
- '2a03|2a12|2a13',
+ '2a03|2a12|2a13|2a42|2e02|2e12|2e22|2e32|2e42|2e92|a001|a011',
'code' => '',
'process' => 'Intel 65n',
'years' => '2006-07',
@@ -18162,17 +18384,18 @@ sub set_intel_data {
'process' => 'Intel 10nm',
'years' => '2019-21',
},
- {'arch' => 'Gen-12.1',
- 'ids' => '4905|4907|4908|4909|4c8a|4c8b|4c90|4c9a|9a40|9a49|9a59|9a60|9a68|' .
- '9a70|9a78|9ac0|9ac9|9ad9|9af8',
+ {'arch' => 'Gen-12.1',
+ 'ids' => '4626|4628|462a|4636|4638|463a|4680|4682|4688|468a|468b|4690|4692|' .
+ '4693|46a1|46a2|46a3|46b2|46b3|46c2|46c3|46d0|46d1|46d2|4905|4907|4908|4909|' .
+ '4c8a|4c8b|4c90|4c9a|9a40|9a49|9a59|9a60|9a68|9a70|9a78|9ac0|9ac9|9ad9|9af8|' .
+ 'a719|a720|a721|a780|a781|a782|a783|a788|a789|a78a|a78b|a7a8|a7a9',
'code' => '',
'process' => 'Intel 10nm',
'years' => '2020-21',
},
{'arch' => 'Gen-12.2',
- 'ids' => '4626|4628|462a|4636|4638|463a|4680|4682|4688|468a|468b|4690|4692|' .
- '4693|46a0|46a1|46a2|46a3|46a6|46a8|46aa|46b0|46b1|46b2|46b3|46b6|46b8|46ba|' .
- '46c0|46c1|46c2|46c3|46d0|46d1|46d2|46d3|46d4',
+ 'ids' => '4626|46a0|46a1|46a6|46a8|46aa|46b0|46b1|46b6|46b8|46ba|46c0|46c1|' .
+ '46d3|46d4',
'code' => '',
'process' => 'Intel 10nm',
'years' => '2021-22+',
@@ -18205,9 +18428,15 @@ sub set_intel_data {
'years' => '2023+',
},
{'arch' => 'Gen-14',
+ 'ids' => 'e202|e20b|e20c|e20d|e212',
+ 'code' => '',
+ 'process' => 'TSMC n4 (4nm)',
+ 'years' => '2024+',
+ },
+ {'arch' => 'Gen-14',
'ids' => '6420|64a0|64b0',
'code' => '',
- 'process' => 'TSMC 3nm',
+ 'process' => 'TSMC n3 (3nm)',
'years' => '2024+',
},
{'arch' => 'Gen-15',
@@ -18402,8 +18631,8 @@ sub set_nv_data {
'years' => '2010-2016',
},
## Legacy 470.xx
- {'arch' => 'Fermi 2',
- 'ids' => '0fec|1281|1289|128b|1295|1298',
+ {'arch' => 'Fermi-2',
+ 'ids' => '0fec|1281|1289|128b|1295',
'code' => 'GF119/GK208',
'kernel' => '',
'legacy' => 1,
@@ -18416,7 +18645,7 @@ sub set_nv_data {
},
# GT 720M and 805A/810A are the same cpu id.
# years: 2012-2018 Kepler 2013-2015 Kepler 2.0
- {'arch' => 'Kepler',
+ {'arch' => 'Kepler-2',
'ids' => '0fc6|0fc8|0fc9|0fcd|0fce|0fd1|0fd2|0fd3|0fd4|0fd5|0fd8|0fd9|0fdf|' .
'0fe0|0fe1|0fe2|0fe3|0fe4|0fe9|0fea|0fed|0fee|0ff6|0ff8|0ff9|0ffa|0ffb|0ffc|' .
'0ffd|0ffe|0fff|1001|1004|1005|1007|1008|100a|100c|1021|1022|1023|1024|1026|' .
@@ -18424,7 +18653,7 @@ sub set_nv_data {
'118e|118f|1193|1194|1195|1198|1199|119a|119d|119e|119f|11a0|11a1|11a2|11a3|' .
'11a7|11b4|11b6|11b7|11b8|11ba|11bc|11bd|11be|11c0|11c2|11c3|11c4|11c5|11c6|' .
'11c8|11cb|11e0|11e1|11e2|11e3|11fa|11fc|1280|1282|1284|1286|1287|1288|1290|' .
- '1291|1292|1293|1295|1296|1299|129a|12b9|12ba',
+ '1291|1292|1293|1295|1296|1298|1299|129a|12b9|12ba',
'code' => 'GKxxx',
'kernel' => '',
'legacy' => 1,
@@ -18448,7 +18677,7 @@ sub set_nv_data {
'legacy' => 0,
'process' => 'TSMC 28nm',
'release' => '',
- 'series' => '545.xx+',
+ 'series' => '550.xx+',
'status' => main::message('nv-current-eol',$date,'2026-12-xx'),
'xorg' => '',
'years' => '2014-2019',
@@ -18465,7 +18694,7 @@ sub set_nv_data {
'legacy' => 0,
'process' => 'TSMC 16nm',
'release' => '',
- 'series' => '545.xx+',
+ 'series' => '550.xx+',
'status' => main::message('nv-current-eol',$date,'2026-12-xx'),
'xorg' => '',
'years' => '2016-2021',
@@ -18477,7 +18706,7 @@ sub set_nv_data {
'legacy' => 0,
'process' => 'TSMC 12nm',
'release' => '',
- 'series' => '545.xx+',
+ 'series' => '550.xx+',
'status' => main::message('nv-current-eol',$date,'2026-12-xx'),
'xorg' => '',
'years' => '2017-2020',
@@ -18507,7 +18736,7 @@ sub set_nv_data {
'24b1|24b6|24b7|24b8|24b9|24ba|24bb|24c7|24c9|24dc|24dd|24e0|24fa|2503|2504|' .
'2507|2508|2520|2521|2523|2531|2544|2560|2563|2571|2582|2584|25a0|25a2|25a5|' .
'25ab|25ac|25b0|25b2|25b6|25b8|25b9|25ba|25bb|25bc|25bd|25e0|25e2|25e5|25ec|' .
- '25f9|25fa|25fb|2838',
+ '25f9|25fa|25fb|2822|2838',
'code' => 'GAxxx',
'kernel' => '',
'legacy' => 0,
@@ -18519,13 +18748,13 @@ sub set_nv_data {
'years' => '2020-2023',
},
{'arch' => 'Hopper',
- 'ids' => '2321|2322|2324|2329|2330|2331|2339|233a|2342',
+ 'ids' => '2321|2322|2324|2329|2330|2331|2335|2339|233a|2342',
'code' => 'GH1xx',
'kernel' => '',
'legacy' => 0,
'process' => 'TSMC n4 (5nm)',
'release' => '',
- 'series' => '545.xx+',
+ 'series' => '550.xx+',
'status' => $status_current,
'xorg' => '',
'years' => '2022+',
@@ -19095,7 +19324,7 @@ sub tty_data {
}
}
-## LogicalItem
+## LogicalItem ##
{
package LogicalItem;
@@ -19460,7 +19689,7 @@ sub component_recursive_data {
}
}
-## MachineItem
+## MachineItem ##
# public methods: get(), is_vm()
{
my $b_vm;
@@ -20377,7 +20606,7 @@ sub check_vm {
}
}
-## NetworkItem
+## NetworkItem ##
{
package NetworkItem;
my ($b_ip_run,@ifs_found);
@@ -20925,6 +21154,7 @@ sub wan_ip {
my @urls = (!$wan_url) ? qw(http://whatismyip.akamai.com/
http://icanhazip.com/ https://smxi.org/opt/ip.php) : ($wan_url);
foreach (@urls){
+ last if !$dl{'dl'};
$ua = 'ip' if $_ =~ /smxi/;
$ip = main::download_file('stdout',$_,'',$ua);
if ($ip){
@@ -20981,7 +21211,7 @@ sub check_wifi {
}
}
-## OpticalItem
+## OpticalItem ##
{
package OpticalItem;
@@ -21305,7 +21535,7 @@ sub drive_data_linux {
}
}
-## PartitionItem
+## PartitionItem ##
{
# these will be globally accessible via PartitionItem::filters()
my ($fs_exclude,$fs_skip,$part_filter);
@@ -21406,14 +21636,14 @@ sub create_output {
$fs !~ /^$fs_skip$/){
if ($show{'label'}){
if ($use{'filter-label'}){
- $row->{'label'} = main::filter_partition('part', $row->{'label'}, '');
+ main::filter_partition('part', \$row->{'label'}, '');
}
$row->{'label'} ||= 'N/A';
$rows->[$j]{main::key($num++,0,2,'label')} = $row->{'label'};
}
if ($show{'uuid'}){
if ($use{'filter-uuid'}){
- $row->{'uuid'} = main::filter_partition('part', $row->{'uuid'}, '');
+ main::filter_partition('part', \$row->{'uuid'}, '');
}
$row->{'uuid'} ||= 'N/A';
$rows->[$j]{main::key($num++,0,2,'uuid')} = $row->{'uuid'};
@@ -22221,7 +22451,7 @@ sub get_uuid {
}
}
-## ProcessItem
+## ProcessItem ##
{
package ProcessItem;
# header:
@@ -22398,7 +22628,7 @@ sub throttled {
}
}
-## RaidItem
+## RaidItem ##
{
package RaidItem;
@@ -23663,7 +23893,7 @@ sub check_zfs_status {
}
}
-## RamItem
+## RamItem ##
{
package RamItem;
my ($speed_maps,$vendors,$vendor_ids);
@@ -25209,7 +25439,7 @@ sub ram_vendor {
}
}
-## RepoItem
+## RepoItem ##
{
package RepoItem;
# easier to keep these package global, but undef after done
@@ -26294,7 +26524,7 @@ sub file_path {
}
}
-## SensorItem
+## SensorItem ##
{
package SensorItem;
my $gpu_data = [];
@@ -27702,7 +27932,7 @@ sub gpu_sensor_data {
}
}
-## SlotItem
+## SlotItem ##
{
package SlotItem;
my ($sys_slots);
@@ -28012,7 +28242,7 @@ sub slot_children_recursive {
}
}
-## SwapItem
+## SwapItem ##
{
package SwapItem;
@@ -28119,14 +28349,14 @@ sub create_output {
}
if ($show{'label'} && ($row->{'label'} || $row->{'swap-type'} eq 'partition')){
if ($use{'filter-label'}){
- $row->{'label'} = main::filter_partition('part', $row->{'label'}, '');
+ main::filter_partition('part', \$row->{'label'}, '');
}
$row->{'label'} ||= 'N/A';
$rows->[$j]{main::key($num++,0,2,'label')} = $row->{'label'};
}
if ($show{'uuid'} && ($row->{'uuid'} || $row->{'swap-type'} eq 'partition')){
if ($use{'filter-uuid'}){
- $row->{'uuid'} = main::filter_partition('part', $row->{'uuid'}, '');
+ main::filter_partition('part', \$row->{'uuid'}, '');
}
$row->{'uuid'} ||= 'N/A';
$rows->[$j]{main::key($num++,0,2,'uuid')} = $row->{'uuid'};
@@ -28136,7 +28366,7 @@ sub create_output {
}
}
-## UnmountedItem
+## UnmountedItem ##
{
package UnmountedItem;
@@ -28234,14 +28464,14 @@ sub create_output {
if (($show{'label'} || $show{'uuid'}) && $fs !~ /^$fs_skip$/){
if ($show{'label'}){
if ($use{'filter-label'}){
- $row->{'label'} = main::filter_partition('part', $row->{'label'}, '');
+ main::filter_partition('part', \$row->{'label'}, '');
}
$row->{'label'} ||= 'N/A';
$rows->[$j]{main::key($num++,0,2,'label')} = $row->{'label'};
}
if ($show{'uuid'}){
if ($use{'filter-uuid'}){
- $row->{'uuid'} = main::filter_partition('part', $row->{'uuid'}, '');
+ main::filter_partition('part', \$row->{'uuid'}, '');
}
$row->{'uuid'} ||= 'N/A';
$rows->[$j]{main::key($num++,0,2,'uuid')} = $row->{'uuid'};
@@ -28442,7 +28672,7 @@ sub unmounted_filesystem {
}
}
-## UsbItem
+## UsbItem ##
{
package UsbItem;
@@ -28588,7 +28818,7 @@ sub usb_output {
}
}
-## WeatherItem
+## WeatherItem ##
# add metric / imperial (us) switch
{
package WeatherItem;
@@ -29278,6 +29508,7 @@ sub get_location {
$loc_arg = $loc{'zip'};
}
$country = ($loc{'country3'}) ? $loc{'country3'} : $loc{'country'};
+ $country ||= 'Country N/A';
$city = ($loc{'city'}) ? $loc{'city'} : 'City N/A';
$state = ($loc{'region-id'}) ? $loc{'region-id'} : 'Region N/A';
$loc_string = main::filter("$city, $state, $country");
@@ -29566,7 +29797,7 @@ sub set_dboot_data {
eval $end if $b_log;
}
-## DesktopData
+## DesktopData ##
# returns array:
# 0: desktop name
# 1: version
@@ -29958,6 +30189,7 @@ sub de_env_data {
my @desktops =(
[1,'unity','unity','',''],
[0,'budgie','budgie-desktop','','gtk'],
+ [1,'cosmic','cosmic-session','','iced'],
# debian package: lxde-core.
# NOTE: some distros fail to set XDG data for root, ps may get it
[1,'lxde','lxpanel','','gtk-na',',^lxsession$'], # no gtk v data, not same as system
@@ -30096,7 +30328,7 @@ sub tk_gtk_data {
eval $end if $b_log;
}
-# This handles stray tooltips that won't get versions, yet anyway.
+# This handles stray toolkits that won't get versions, yet anyway.
sub tk_misc_data {
eval $start if $b_log;
if ($tk_test eq 'gtk-na'){
@@ -30426,7 +30658,7 @@ sub set_xprop {
}
}
-## DeviceData
+## DeviceData ##
# creates arrays: $devices{'audio'}; $devices{'graphics'}; $devices{'hwraid'};
# $devices{'network'}; $devices{'timer'} and local @devices for logging/debugging
# 0: type
@@ -31218,7 +31450,7 @@ sub get_device_temp {
return $temp;
}
-## DiskDataBSD
+## DiskDataBSD ##
# handles disks and partition extra data for disks bsd, raid-zfs,
# partitions, swap, unmounted
# glabel: partID, logical/physical-block-size, uuid, label, size
@@ -31533,7 +31765,7 @@ sub set_gpart_data {
}
}
-## DmData
+## DmData ##
# Public method: get()
# returns hash ref of array of arrays for dm/lm
# hash: dm, lm
@@ -31598,7 +31830,8 @@ sub get_dm_lm {
lightdm loginx lxdm ly mdm mlogind nodm pcdm qingy sddm slim slimski tdm
udm wdm x3dm xdm xdmctl xenodm);
}
- # greetd frontends: agreety dlm gtkgreet qtgreet tuigreet wlgreet
+ # greetd frontends: agreety cosmic-greeter dlm gtkgreet qtgreet tuigreet
+ # wlgreet
# slick, elephant greeters for lightdm so aren't really lm
else {
@dms = qw(elogind greetd qtgreet seatd tbsm);
@@ -31655,7 +31888,7 @@ sub test_ps_dm {
}
}
-## DistroData
+## DistroData ##
{
package DistroData;
my ($id_src,@osr,@working);
@@ -32640,7 +32873,7 @@ sub dbg_distro_files {
}
}
-## DmidecodeData
+## DmidecodeData ##
{
package DmidecodeData;
@@ -32749,7 +32982,8 @@ sub get_driver_modules {
return $modules;
}
-## GlabelData: public methods: get()
+## GlabelData ##
+# public methods: get()
# Used to partitions, swap, RAID ZFS gptid path standard name, like ada0p1
{
package GlabelData;
@@ -32813,7 +33047,7 @@ sub get_hostname {
return $hostname;
}
-## InitData
+## InitData ##
{
package InitData;
my ($init,$init_version,$program) = ('','','');
@@ -33024,7 +33258,7 @@ sub get_runlevel_default {
}
}
-## IpData
+## IpData ##
{
package IpData;
@@ -33042,7 +33276,8 @@ sub set {
sub set_ip_addr {
eval $start if $b_log;
- my @data = main::grabber($alerts{'ip'}->{'path'} . " addr 2>/dev/null",'\n','strip');
+ my ($b_skip,$broadcast,$if,$if_id,$ip,$scope,$type);
+ my (@data,@ips,@temp);
if ($fake{'ip-if'}){
# my $file = "$fake_data_dir/if/scope-ipaddr-1.txt";
# my $file = "$fake_data_dir/network/ip-addr-blue-advance.txt";
@@ -33051,7 +33286,9 @@ sub set_ip_addr {
# my $file = "$fake_data_dir/network/ppoe/ppoe-ip-addr-3.txt";
# @data = main::reader($file,'strip') or die $!;
}
- my ($b_skip,$broadcast,$if,$if_id,$ip,@ips,$scope,$type,@temp,@temp2);
+ else {
+ @data = main::grabber($alerts{'ip'}->{'path'} . " addr 2>/dev/null",'\n','strip');
+ }
foreach (@data){
if (/^[0-9]/){
# print "$_\n";
@@ -33096,15 +33333,17 @@ sub set_ip_addr {
sub set_ifconfig {
eval $start if $b_log;
+ my ($b_skip,$broadcast,$duplex,$if,$if_id,$ip,$mac,$scope,$speed,$state,$type);
+ my (@data,@ips,@ips_bsd,@temp);
# whitespace matters!! Don't use strip
- my @data = main::grabber($alerts{'ifconfig'}->{'path'} . " 2>/dev/null",'\n','');
if ($fake{'ip-if'}){
# my $file = "$fake_data_dir/network/ppoe/ppoe-ifconfig-all-1.txt";
# my $file = "$fake_data_dir/network/vps-ifconfig-1.txt";
# @data = main::reader($file) or die $!;
}
- my ($b_skip,$broadcast,$if,@ips_bsd,$ip,@ips,$scope,$if_id,$type,@temp,@temp2);
- my ($state,$speed,$duplex,$mac);
+ else {
+ @data = main::grabber($alerts{'ifconfig'}->{'path'} . " 2>/dev/null",'\n','');
+ }
foreach (@data){
if (/^[\S]/i){
# print "$_\n";
@@ -33239,7 +33478,7 @@ sub get_kernel_clocksource {
eval $end if $b_log;
}
-## KernelCompiler
+## KernelCompiler ##
{
package KernelCompiler;
@@ -33352,7 +33591,7 @@ sub get_kernel_data {
return $kernel;
}
-## KernelParameters
+## KernelParameters ##
{
package KernelParameters;
@@ -33387,7 +33626,8 @@ sub parameters_bsd {
}
}
-## LsblkData: public methods: set(), get()
+## LsblkData ##
+# public methods: set(), get()
{
package LsblkData;
@@ -33479,7 +33719,7 @@ sub set_mapper {
eval $end if $b_log;
}
-## MemoryData
+## MemoryData ##
{
package MemoryData;
@@ -34127,7 +34367,7 @@ sub get_module_version {
return $version;
}
-## PackageData
+## PackageData ##
# Note: this outputs the key/value pairs ready to go and is
# called from either -r or -Ix, -r precedes.
{
@@ -34421,7 +34661,7 @@ sub count_libs {
}
}
-## ParseEDID
+## ParseEDID ##
{
package ParseEDID;
# CVT_ratios:
@@ -35129,7 +35369,8 @@ sub _round {
}
}
-## PartitionData: public methods: set(), get()
+## PartitionData ##
+# public methods: set(), get()
# for /proc/partitions only, see DiskDataBSD for BSD partition data.
{
package PartitionData;
@@ -35276,7 +35517,8 @@ sub get_pcie_data {
eval $end if $b_log;
}
-## PowerData: public method: get()
+## PowerData ##
+# public method: get()
# No BSD support currently. Test by !$bsd_type. Should any BSD data source
# appear, make bsd_data() and add $bsd_type switch here, remove from caller.
{
@@ -35341,7 +35583,7 @@ sub sys_data {
}
}
-# ProgramData
+# ProgramData ##
# public methods:
# full(): returns (print name, version nu, [full version data output]).
# values(): returns program values array
@@ -35442,7 +35684,8 @@ sub set_values {
'comfc' => ['^comfc',0,'0','comfc',0,1,0,'',''], # unverified
'compiz' => ['^compiz',2,'--version','Compiz',0,1,0,'',''],
'compton' => ['^\d',1,'--version','Compton',0,1,0,'',''],
- 'cosmic-comp' => ['^cosmic-comp',0,'0','cosmic-comp',0,1,0,'',''], # unverified
+ 'cosmic-comp' => ['^cosmic-comp',0,'0','cosmic-comp',0,1,0,'',''], # cosmic alpha, no versions
+ 'cosmic-session' => ['^cosmic-session',0,'0','Cosmic',0,1,0,'',''], # cosmic alpha, no versions
'ctwm' => ['^\S',1,'-version','ctwm',0,1,0,'',''],
'cwm' => ['^cwm',0,'0','CWM',0,1,0,'',''], # no version
'dawn' => ['^dawn',1,'-v','dawn',0,1,1,'^dawn-',''], # to stderr, not verified
@@ -35680,6 +35923,7 @@ sub set_values {
'cdm' => ['^cdm',0,'0','CDM',0,1,0,'',''],
# might be xlogin, unknown output for -V
'clogin' => ['^clogin',0,'-V','clogin',0,1,0,'',''], # unverified, cysco router
+ 'cosmic-greeter' => ['^cosmic-greeter',0,'0','cosmic-greeter',0,1,0,'',''], # no version, uses greetd
'elephant-greeter' => ['^elephant',0,'0','elephant-greeter',0,1,0,'',''], # unverified, lightdm greeter
'elogind' => ['^elogind',0,'0','elogind',0,1,0,'',''], # no version
'emptty' => ['^emptty',0,'0','EMPTTY',0,1,0,'',''], # unverified
@@ -35938,7 +36182,7 @@ sub version_pkg {
}
}
-## PsData
+## PsData ##
# public methods:
# set(): sets @ps_aux, @ps_cmd
# set_dm(): sets $ps_data{'dm-active'}
@@ -35950,14 +36194,21 @@ package PsData;
sub set {
eval $start if $b_log;
- my ($b_busybox,$header,$ps,@temp);
+ my ($b_busybox,$header,$link,$path,$ps,@temp);
$loaded{'ps-data'} = 1;
my $args = 'wwaux';
- my $path = main::check_program('ps');
- my $link = readlink($path);
- if ($link && $link =~ /busybox/i){
- $b_busybox = 1;
- $args = '';
+ # it's possible ps isn't even installed
+ if ($path = main::check_program('ps')){
+ $link = readlink($path);
+ if ($link && $link =~ /busybox/i){
+ $b_busybox = 1;
+ $args = '';
+ }
+ }
+ else {
+ main::log_data('data','No ps installed.') if $b_log;
+ eval $end if $b_log;
+ return;
}
# note: some ps cut output based on terminal width, ww sets width unlimited
# old busybox returns error with args, new busybox ignores auxww
@@ -35969,6 +36220,8 @@ sub set {
@temp = split(/\s+/, $header);
}
else {
+ main::log_data('data','@$ps is empty.') if $b_log;
+ eval $end if $b_log;
return;
}
$ps_data{'header'}->[0] = $#temp; # the indexes, not the scalar count
@@ -36033,7 +36286,7 @@ sub set_de_wm {
if ($show{'system'}){
# some desktops detect via ps as fallback
process_items(\@{$ps_data{'de-ps-detect'}},join('|', qw(
- razor-desktop razor-session lxsession lxqt-session nscde
+ cosmic-session razor-desktop razor-session lxsession lxqt-session nscde
tdelauncher tdeinit_phase1)));
# order matters!
process_items(\@{$ps_data{'wm-parent'}},join('|', qw(xfdesktop icewm fluxbox
@@ -36061,14 +36314,16 @@ sub set_de_wm {
if ($show{'graphic'}){
$b_de_wm_comp = 1;
$b_wm_comp = 1;
- process_items(\@{$ps_data{'compositors-pure'}},join('|',qw(cairo compton dcompmgr
- mcompositor picom steamcompmgr surfaceflinger xcompmgr unagi)));
+ process_items(\@{$ps_data{'compositors-pure'}},join('|',qw(cairo compton
+ cosmic-comp dcompmgr mcompositor picom steamcompmgr surfaceflinger
+ xcompmgr unagi)));
}
if ($b_de_wm_comp){
process_items(\@{$ps_data{'de-wm-compositors'}},join('|',qw(budgie-wm compiz
- deepin-kwin_wayland deepin-kwin_x11 deepin-wm enlightenment gala gnome-shell
- twin kwin_wayland kwin_x11 kwinft kwin marco deepin-metacity metacity
- metisse mir moksha muffin deepin-mutter mutter ukwm xfwm[345]?)));
+ deepin-kwin_wayland deepin-kwin_x11 deepin-wm enlightenment
+ gala gnome-shell twin kwin_wayland kwin_x11 kwinft kwin marco
+ deepin-metacity metacity metisse mir moksha muffin deepin-mutter mutter
+ ukwm xfwm[345]?)));
}
if ($b_wm_comp){
# x11: 3dwm, qtile [originally], rest wayland
@@ -36145,7 +36400,8 @@ sub set_power {
process_items(\@{$ps_data{'power-services'}},join('|', qw(apmd csd-power
gnome-power-manager gsd-power kpowersave org\.dracolinux\.power
org_kde_powerdevil mate-power-manager power-profiles-daemon powersaved
- tdepowersave thermald tlp upowerd ukui-power-manager xfce4-power-manager)));
+ system76-power tdepowersave thermald tlp upowerd ukui-power-manager
+ xfce4-power-manager)));
print '$ps_data{power-daemons}: ', Data::Dumper::Dumper $ps_data{'power-services'} if $dbg[5];
main::log_data('dump','$ps_data{power-daemons}',$ps_data{'power-services'}) if $b_log;
eval $end if $b_log;
@@ -36175,7 +36431,7 @@ sub get_self_version {
return $self_version . $patch;
}
-## ServiceData
+## ServiceData ##
{
package ServiceData;
my ($key,$service,$type);
@@ -36381,7 +36637,7 @@ sub set {
}
# $dbg[29] = 1; set_path(); print ServiceData::get('status','bluetooth'),"\n";
-## ShellData
+## ShellData ##
{
package ShellData;
my $b_debug = 0; # disable all debugger output in case forget to comment out!
@@ -36859,7 +37115,7 @@ sub get_uptime {
return $uptime;
}
-## UsbData
+## UsbData ##
# %usb array indexes
# 0: bus id / sort id
# 1: device id
@@ -38009,7 +38265,7 @@ sub process_power {
#### GENERATE OUTPUT
########################################################################
-## OutputGenerator
+## OutputGenerator ##
# Also creates Short, Info, and System items
{
package OutputGenerator;
@@ -38460,10 +38716,12 @@ sub system_item {
if ($b_admin && (my $params = KernelParameters::get())){
# print "$params\n";
if ($use{'filter-label'}){
- $params = main::filter_partition('system', $params, 'label');
+ main::filter_partition('system', \$params, '=LABEL=');
}
if ($use{'filter-uuid'}){
- $params = main::filter_partition('system', $params, 'uuid');
+ main::filter_partition('system', \$params, '=UUID=');
+ main::filter_partition('system', \$params, 'systemd.machine_id=');
+
}
$data->{$data_name}[$index]{main::key($num++,0,2,'parameters')} = $params;
diff --git a/inxi.1 b/inxi.1
index 1c30ffb..43b86b5 100644
--- a/inxi.1
+++ b/inxi.1
@@ -15,7 +15,7 @@
.\" with this program; if not, write to the Free Software Foundation, Inc.,
.\" 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
.\"
-.TH INXI 1 "2024\-06-18"inxi" "inxi manual"
+.TH INXI 1 "2024\-09-04" "inxi 3.3.36" "inxi manual"
.SH NAME
inxi \- Command line system information script for console and IRC
@@ -520,9 +520,9 @@ unless you figure out how to set up doas/sudo to permit dmidecode to read
\fB/dev/mem\fR as user.
\fBudevadm\fR can be run by non\-superuser, or if dmidecode is not installed
-(Linux only). It has a slightly less reliable dmi table outut, and does not seem
-to support more than 1 board memory array, but is pretty good. Voltages may be
-wrong however.
+(Linux only). It has a slightly less reliable dmi table output, and does not
+seem to support more than 1 board memory array, but is pretty good. Voltages may
+be wrong however.
Both \fBdmidecode\fR and \fBudevadm\fR need a DMI table with RAM data to create
the report. Most SBC/SOC boards don't have dmi based RAM data. But most other
@@ -2105,17 +2105,25 @@ common).
to this line from \fBInfo:\fR line.
Topology line contains, if available and/or relevant: physical CPU count
-(\fBcpus:\fR); per physical cpu core count (cores:\fR); threads per core, if > 1
-(\fBtpc:\fR); how many \fBthreads:\fR (if more threads than cores); \fBdies:\fR
-(rarely detected, but if so, if > 1); smt status (if no smt status found, shows
-\fBN/A\fR).
-
-If complex CPU type, like Alder lake, cores; will have a more granular breakdown
-of how many mt (multi\-threaded) and how many st (single\-threaded) cores there
-in the physical cpu ( \fBmt\-cores:\fR, \fBst\-cores:\fR); For complex CPU
-types like ARM SoC devices with 2 CPU types, with different core counts and/or
-\fBmin/max:\fR) frequencies, \fBvariant:\fR per type found, with relevant
-differences shown, like \fBcores:\fR, \fBmin/max:\fR, etc.
+(\fBcpus:\fR); per physical CPU \fBdies:\fR, \fBclusters:\fR, \fBcores:\fR;
+threads per core, if > 1 (\fBtpc:\fR); how many \fBthreads:\fR (if more threads
+than cores); smt status (if no smt status found, shows \fBN/A\fR).
+
+Not all CPUs have or report dies or clusters. Some may have dies but no
+clusters, some clusters but no dies, some dies and clusters, and some neither
+dies nor clusters. This is a function of how the CPU topology reports itself to
+the kernel. Note that core counts are per physical CPU, not per die or cluster.
+Clusters are per die, and in cases of > 1 dies, will show as: \fBclusters:
+2x4\fR.
+
+If complex CPU type, like Alder lake, \fBcores:\fR will have a more granular
+breakdown of how many mt (multi\-threaded) and how many st (single\-threaded)
+cores there are in the physical cpu (\fBmt\-cores:\fR, \fBst\-cores:\fR); For
+complex CPU types like ARM SoC devices with 2 CPU types, with different core
+counts and/or \fBmin/max:\fR) frequencies, \fBvariant:\fR per type found, with
+relevant differences shown, like \fBcores:\fR, \fBmin/max:\fR, etc.
+
+
.nf
\fBCPU:
@@ -2135,10 +2143,10 @@ differences shown, like \fBcores:\fR, \fBmin/max:\fR, etc.
microcode: 0x8001250
Topology:
cpus: 2
+ dies: 4
cores: 16
- tpc: 2
threads: 32
- dies: 4
+ tpc: 2
cache:
L1: 2x 1.5 MiB (3 MiB)
desc: d\-16x32 KiB; i\-16x64 KiB
@@ -2161,6 +2169,64 @@ differences shown, like \fBcores:\fR, \fBmin/max:\fR, etc.
bogomips: 267823\fR
.fi
+Or this Raptor Lake with 1 die, 4 clusters, and efficiency and perforance cores:
+
+.nf
+\fBCPU:
+ Info:
+ model: 13th Gen Intel Core i5-1345U
+ bits: 64
+ type: MST AMCP
+ arch: Raptor Lake
+ level: v3
+ note: check
+ built: 2022+
+ process: Intel 7 (10nm)
+ family: 6
+ model-id: 0xBA (186)
+ stepping: 3
+ microcode: 0x411C
+ Topology:
+ cpus: 1
+ dies: 1
+ clusters: 4
+ cores: 10
+ threads: 12
+ mt: 2
+ tpc: 2
+ st: 8
+ smt: enabled
+ cache:
+ L1: 928 KiB
+ desc: d-8x32 KiB, 2x48 KiB; i-2x32 KiB, 8x64 KiB
+ L2: 6.5 MiB
+ desc: 2x1.2 MiB, 2x2 MiB
+ L3: 12 MiB
+ desc: 1x12 MiB
+ Speed (MHz):
+ avg: 1535
+ high: 2820
+ min/max: 400/4700:3500
+ scaling:
+ driver: intel_pstate
+ governor: powersave
+ cores:
+ 1: 0
+ 2: 400
+ 3: 429
+ 4: 926
+ 5: 1244
+ 6: 1139
+ 7: 2680
+ 8: 1021
+ 9: 2582
+ 10: 2744
+ 11: 2820
+ 12: 2445
+ bogomips: 59904
+ Flags: avx avx2 ht lm nx pae sse sse2 sse3 sse4_1 sse4_2 ssse3 vmx\fR
+.fi
+
\- Adds CPU Vulnerabilities (bugs) as known by your current kernel. Lists by
\fBType: ... (status|mitigation): ....\fR for systems that support this feature
(Linux kernel 4.14 or newer, or patched older kernels).
diff --git a/inxi.changelog b/inxi.changelog
index 51a5202..9022a79 100644
--- a/inxi.changelog
+++ b/inxi.changelog
@@ -1,4 +1,200 @@
================================================================================
+Version: 3.3.36
+Patch: 00
+Date: 2024-09-04
+--------------------------------------------------------------------------------
+RELEASE NOTES:
+--------------------------------------------------------------------------------
+
+Phase 2 of the big CPU upgrade a few years back is now done, I'd left one part
+inadequate in terms of the data structures, mainly because I did not have data
+samples to test, but also because no corner cases that required a more robust
+data structure for die > cluster > core counts showed up duing the initial
+development phase. This led to somewhat predictable issues and bug reports when
+someone had a CPU that did require that structure to show correct core/die type
+data.
+
+So while most users will see very little difference, beyond dies: and clusters:
+items appearing where they did not before, the internal logic is now far more
+robust.
+
+--------------------------------------------------------------------------------
+SPECIAL THANKS:
+
+1. CPU: Codeberg issue #307 finally helped solve the old AMD ryzen wrong core
+count issue, I believe. Thanks superkoning for the data and reporting the
+RISC-V failure to show right core counts, and to show MT when it isn't, which
+was caused by same issue.
+
+2. RECOMMENDS: Codeberg user Ricky-Tigg in issue #309 for taking the time to
+actually check --recommends output for inconsistencies. And finding them!
+
+--------------------------------------------------------------------------------
+KNOWN ISSUES:
+
+1. The old issue #293 about AMD Ryzen 2 die CPU showing wrong core counts is
+probably fixed, but can't know until it's confirmed for that exact CPU. Fix 1a
+should handle all variants, I hope. Tested on old OS VM installs, and found most
+of this logic never fires since /sys had a very barebones topology section f for
+CPUs back then.
+
+2. DESKTOP: No known way to get Cosmic DE version, or Iced toolkit version. If
+you know of a non-hackish way to get that version info, let me know.
+
+3. DRIVES: The disk vendor pinxi/tools/lists/disks.unhandled list keeps getting
+longer, but most are not possible match, either not unique, or unknown models.
+Doing these matches is incredibly tedious, so I only do it every few releases,
+but this time there were a LOT of new matches and vendors.
+
+--------------------------------------------------------------------------------
+BUGS:
+
+1. CPU: wrong core counts due to inadequate internal logic, and some new uses of
+clusters to categorize core ids. Since this is a pretty new change in the kernel
+topology logic, this wasn't really a bug per se, but it looks like one to users.
+
+--------------------------------------------------------------------------------
+FIXES:
+
+1a. CPU: At long last, codeberg issue #307 supplied the data of a multi-die or
+more accurately, a multi-cluster but no die_id, system. RISC-V in this case,
+which tripped the wrong core count, and a false MT type, because it had > 1
+cluster, but no die. Each cluster repeated the same core_ids.
+
+That's the same issue I believe the old AMD Ryzen issue had, but I never got the
+required data for that. Had to redo the entire /sys CPU data structure to be:
+phyical_ids > die_ids > cluster_ids > core_ids, which was very tricky to do.
+
+1b. CPU: cpu_arch: fixed some AMD Zen generations, and process nodes, had
+estimated from roadmap, updated with real.
+
+2a. RECOMMENDS: issue #309 notes ifconfig wasn't clearly ID'ed as:
+(deprecated, ip preferred)
+That's been corrected.
+
+2b. RECOMMENDS: issue #309 also noted that lspci is not listed, which was an
+oversight, given the equivalent BSD tools are tested for BSDs. I have no idea
+how that was missed all these years!
+
+2c. RECOMMENDS: added ps test, found a case where that's not installed.
+
+3. GRAPHICS: GPU data: fixed an nvidia 470 Kepler2/Fermi2 ID error. And also
+corrected the arch name, it's Kepler-2, not Kepler.
+
+--------------------------------------------------------------------------------
+ENHANCEMENTS:
+
+1. MACHINE: Added systemd.machine_id to --filter-uuid filters. Thanks Malcolm
+from Opensuse for pointing out that one.
+
+2a. CPU: Now shows clusters, if found. Clusters seem related to L2 assignment,
+but not always. When no die_id or cluster_id found, won't show that item.
+
+2b. CPU: New AMD, Intel CPU arch models.
+
+3a. GRAPHICS: added cosmic-comp compositor support. No current known way to get
+version.
+
+3b. GRAPHICS: new gpu ids for intel, nvidia.
+
+4. SYSTEM: Desktop: Added basic Cosmic DE detection. Uses XDG for detection,
+and cosmic-session for possible future version.
+
+5. INFO: Power: Added system76-power power manager.
+
+6. DRIVES: a huge disk vendor update. New vendors, new matches for existing!
+It's surreal how many SSD and USB makers there are out in the world. It's like
+running a corner store used to be or something. There are a LOT of new matches
+and vendors this time around!
+
+--------------------------------------------------------------------------------
+CHANGES:
+
+1. CPU: Moved the new dies:/clusters: block to right after the cpu physical
+count, and before the core count. This makes the order of output match the order
+of organization: physical cpus > dies > cluster > cores. Also if it got a
+positive die_id or cluster_id, will show how many found always for -Ca. Before
+it only showed if greater than 1 die.
+
+--------------------------------------------------------------------------------
+DOCUMENTATION:
+
+1a. MAN: Debian Lintian tests found a typo! That's fixed. I would have expected
+many more than 1 typos, so that's good to see.
+
+1b. MAN: A missing space and extra " slightly broke the header:
+.TH INXI 1 "2024\-06-27"inxi" "inxi manual"
+should be:
+.TH INXI 1 "2024\-06-27" inxi "inxi manual"
+All that happens due to this is bottom of man output shows:
+inxi" 2024-06-18 INXI(1)
+so no big deal, but it's corrected anyway.
+
+1c. MAN: Also added inxi version to man info line, I noticed other programs do
+that. One more thing to forget during upgrades heh. Maybe I'll add that to
+release.pl since I'm sure to forget it.
+
+1d. MAN: Updated to show clusters added, and to show cluster sample for -C a.
+Also added more complete explanation of when dies/clusters show, and how core
+and cluster counts work.
+
+2a. OPTIONS: Fixed typo, thanks codeberg PR. Note inxi branch is not for PR,
+only pinxi. But good to catch typos anyway.
+
+2b. OPTIONS: Added dies, clusters to -Ca, now that dies/clusters always show if
+found, regardless of number.
+
+3a. DOCS: docs/inxi-cpu.txt: had neglected to add in the advanced sys/cpuinfo
+pairs debugger code from the initial Slackware LQ forums threads. Added in
+CPU DEGUGGING primary section, with that code, which is 2 1 liners, could be 1.
+
+This is same basic logic the --debug 2x debugger collects, but sometimes it's
+easier to just get the two files we need from people.
+
+--------------------------------------------------------------------------------
+CODE:
+
+1. simplified main::filter_partitions, main::filter_pci_long. Now takes string
+by reference, and removes all copies, so those are more efficient now.
+
+2a. CPU: finally added in advanced topology structure phys > dies > clusters >
+cores to the main cpu_sys data structure, I'd avoided doing that in past because
+it's hard, and I never found a data sample that had repeated core_ids per die,
+until this one. Note while for RISC-V, this fix applies to the entire class of
+possible repeating core_ids per die / cluster.
+
+2b. CPU: CpuItem:: sys_data_grabber(): Cleaned up, added more globbing items for
+/sys data. Needed to get at least cluster_id, but made globbing easier to parse
+and read, and since the cluster stuff is useful, added more related.
+
+This fix may have unintended consequences of breaking other logic, but I believe
+it is only within the /sys data itself.
+
+2c. CPU: refactored ordering of cpu subs, renamed many to be less ambiguous, now
+ordered more consistently. The cpu code fixes made me realize that code was not
+easy to follow in terms of where things were arranged, now it's quite
+consistent, with clear section headers, end section etc.
+
+3. NETWORK: cleaned up code in set_ifconfig(), set_ip();
+
+4. PsData: fixed corner case where no ps is present in system. This trips some
+undefined errors in the grabber.
+
+5. DOWNLOADER: failed to handle case of no curl OR wget, and tried running -U
+without a downloader anyway.
+
+Made the following changes:
+* Network -i: if no dig, and no downloaders, just skips grabbing IPs.
+* Updater -U: if no downloader, exit with error message.
+* Weather -w: if no downloader, will just show no connection available.
+
+6. DATA: tools/lists/gpu.nv.560.raw added. tools/gpu_raw.pl, tools/gpu_ids.pl
+updated for nv 560 files.
+
+--------------------------------------------------------------------------------
+-- Harald Hope - Wed, 4 September 2024 10:15:08 -0700
+
+================================================================================
Version: 3.3.35
Patch: 00
Date: 2024-06-18