From b6ac6026f2d563039211f290ac3bc61a18789274 Mon Sep 17 00:00:00 2001 From: Harald Hope Date: Fri, 7 Oct 2022 19:43:17 -0700 Subject: [PATCH] Another big one, with a long time to-do item done! /sys based sensors data is now used as a fallback, with fully revised error messages to handle this new sensor data variant. Due to potential bugs this might create, this was left off of the 3.3.21 release, which needed to go out on a schedule, but there is plenty of time for 3.3.22 to be debugged. -------------------------------------------------------------------------------- KNOWN ISSUES: 1. inxi can't currently handle raw in[0-9] voltage sensor data from /sys/class/hwmon, that may get corrected, but I've honestly never seen a system that shows raw in[0-9] values as field names, so it's probably not very pressing, but it can happen. Similar that is to how default fanx and tempx field names are processed. 2. Currently only checking -Gx, -Nx device temp for bus IDs ending in .0, which is the primary PCI device. I think that's the only one that will have a temp, .1, which is a second device on the same hardware, doesn't have that data in tests. Saves some requests since it's a big glob of /sys. 3. Spiral Linux has no obvious way to determine that it is Spiral and not Debian 11 as base distro. No /etc/ files for distro ID contain anything for spiral, so leaving that one alone. 4. Can't get 100% reliable cpu level > v2 due to it not being a pure cpu flag based test, which is kind of sadly typical for the originators of this idea, but since the choice was dump the feature, or just use the note: check for > v2, opted for note: check. One wants to ask questions here, but honestly I already know the answer so why bother asking the question... The docs for this are awful, inadequate, incomplete. My strong suspicion is that this is NOT intended to be a distro-wide feature beyond v2 support minimum, but rather is for specific compile options for a package or daemon or server or whatever that can benefit from this type of fine-tuning. One thinks of Gentoo for example back when such fine-tunings could actually deliver noticeable differences in performance. A per system type feature that is, not a distro-wide feature. At least that's my initial feeling, but this is probably about all the time I will spend on it since inxi can't get it more accurate anyway. -------------------------------------------------------------------------------- BUGS: 1. Bug in monitor position logic, the horizontal/vertical sorts were being done alphanumerically, leading to absurd results where 800 > 2560 or whatever. Basically all x / y positions less than 1000 would have forced the smaller number to be considered as the greatest value. Another corner case find by mrmazda. Thanks mrmazda! -------------------------------------------------------------------------------- FIXES: 1. Added i350bb sensor to network sensor type. 2. Small glitch with some scenarios with missing fan1 in sensors, showed fan1 0 rpm, but then showed fan 3: empty. That was a slight error in how undefined vs '' empty was treated. 3. Added fix for defective fan speeds, skip fan item if > 15000, which is a bug in the fan speed report, making it useless. Seen 65535 reported RPM. Could probably make it 10000 upper limit but suspect that is a simple bug that creates an absurd value, 2^16 so won't be anything high unless bug active. This fix runs for ipmi, linux, and sysctl fan data. 4. Trying for fix for dynamic gpu voltage, assumed always mV, but might be V. 5. Inadequate or obscure or non-existent redhat/suse documentation led to some fixes for cpu v levels. Note that level v3/v4 can't be fully determined by cpu flag tests, but who cares? Certainly not me. Added 'note: check' for v3/v4. 6. Nvidia device arch id was too loose, false id for non existing lovelace arch. Note that due to array reverse, the newest ids will always run first, which leads to possible false positives with first string match tests when no product IDs are available yet. -------------------------------------------------------------------------------- ENHANCEMENTS: 1. Elbrus CPU arch, process, year, arch data made more complete using new data resource. Thanks Elbrus guys! 2. Finally, raw, basic /sys/class/hwmon temp data. Linux kernel docs note supports temp, fan, volts, amps, energy. But have only seen temps so far. Can force /sys use with --force sensors-sys / --sensors-sys, though there's no point to doing that except to test. Also changed --recommends to note lm-sensors not required for sensor data now. 3. Adding device temp for -Gx, -Nx. Will only work for Linux and when found, and only for free drivers (I think). 4. Added xdriinfo based dri drivers (with fallback to Xorg.0.log as data source, not as accurate), that will show if and only if that driver is not the same name as a detected X or gpu driver. 5. Another big upgrade to cp_cpu_arch, added and corrected many AMD/Intel matches. 6. A few more gpu product ids, Intel, added. 7. More disk vendors, ids, the list, as we are now well aware, is endless, reflecting perhaps the futility of pursuing the infinite using finite means. -------------------------------------------------------------------------------- CHANGES: 1. Slight changes in how inxi supplies no sensor data messages, and in the fallback cases and handling. More accurate and precise, and more robust overall. 2. Due to complexity of understanding level: and the fact not all cpu flags are exposed that are required, moved -Cxx level: to -Ca. 3. Changing slightly inaccurate Sound Server for ALSA/OSS to Sound API, which is the closest I can come to explaining clearly what it is. Note that you can only load one API type audio subsystem/driver, so you will be running one or the other, never both, from what I understand. Since OpenBSD sndio includes sndiod, calling that a sound server is basically fine, since it's both the server and the interface, if I understand it right, and there won't be a second sound server listed, actually won't be for any BSD that I know of, it's going to be sndio or OSS or nothing, unless something has changed. -------------------------------------------------------------------------------- DOCUMENTATION: 1. Man page, updates for /sys/class/hwmon based sensor data. 2. Small update for cpu level v3/v4, added note: check explanation, though it's too hard to really explain this stuff since the docs are... not wonderful, when they even exist and don't contradict each other. -------------------------------------------------------------------------------- CODE: 1. Refined significantly sensors missing data and error messages to be much more accurate and granular. Also enables more sensors tools, though hopefully they won't appear since those are a real pain to implement, but it's more open to being sensor tool agnostic now due to these refinements than before. 2. Added xdpiinfo to debugger. 3. Switched x_drivers to return ref of array of refs, use join for output only, that lets us use the drivers to test dri stuff also (if we want or need to), and keeps it consistent with how most of inxi does that type of data handling/testing. If undef, it means no array ref exists, which makes testing easy. Not truly understanding hash/array refs when inxi rewrite to Perl started is probably one of the bigger causes of glitches and ongoing optimizations. Basically, in all but very small array cases, it's almost always better to start with a ref from the start as soon as the hash/array moves between functions, with one exception, when it's a globally stored data item. Then it depends. But this requires a consistent testing for null data as well, which is harder if you did it in different ways from the start. But slowly and surely chipping away at these. --- inxi | 983 ++++++++++++++++++++++++++++++++++--------------- inxi.1 | 85 +++-- inxi.changelog | 168 +++++++++ 3 files changed, 912 insertions(+), 324 deletions(-) diff --git a/inxi b/inxi index e071ba5..3fdaf85 100755 --- a/inxi +++ b/inxi @@ -48,8 +48,8 @@ use POSIX qw(ceil uname strftime ttyname); ## INXI INFO ## my $self_name='inxi'; -my $self_version='3.3.21'; -my $self_date='2022-08-22'; +my $self_version='3.3.22'; +my $self_date='2022-10-08'; my $self_patch='00'; ## END INXI INFO ## @@ -1938,6 +1938,7 @@ sub display_data { ['weston','--version'], ['wlr-randr',''], ['xdpyinfo',''], + ['xdriinfo',''], ['Xorg','-version'], ['xprop','-root'], ['xrandr',''], @@ -2945,11 +2946,11 @@ sub check_items { } elsif ($type eq 'recommended display information programs'){ if ($bsd_type){ - @data = qw(glxinfo wmctrl xdpyinfo xprop xrandr); + @data = qw(glxinfo wmctrl xdpyinfo xprop xdriinfo xrandr); $info_os = 'info-bsd'; } else { - @data = qw(glxinfo wmctrl xdpyinfo xprop xrandr); + @data = qw(glxinfo wmctrl xdpyinfo xprop xdriinfo xrandr); } $b_program = 1; $item = 'Program'; @@ -3007,7 +3008,7 @@ sub check_items { } else { @data = qw(/dev /dev/disk/by-id /dev/disk/by-label /dev/disk/by-path - /dev/disk/by-uuid /sys/class/dmi/id); + /dev/disk/by-uuid /sys/class/dmi/id /sys/class/hwmon); } $b_dir = 1; $item = 'Directory'; @@ -3089,9 +3090,6 @@ sub item_data { my ($type) = @_; my $data = { # Directory Data - '/sys/class/dmi/id' => { - 'info' => '-M system, motherboard, bios', - }, '/dev' => { 'info' => '-l,-u,-o,-p,-P,-D disk partition data', }, @@ -3113,6 +3111,12 @@ sub item_data { '/sys' => { 'info' => '', }, + '/sys/class/dmi/id' => { + 'info' => '-M system, motherboard, bios', + }, + '/sys/class/hwmon' => { + 'info' => '-s sensor data (fallback if no lm-sensors)', + }, # File Data '/etc/lsb-release' => { 'info' => '-S distro version data (older version)', @@ -3377,7 +3381,7 @@ sub item_data { 'rpm' => 'systemd or sysvinit', }, 'sensors' => { - 'info' => '-s sensors output', + 'info' => '-s sensors output (optional, /sys supplies most)', 'info-bsd' => '', 'apt' => 'lm-sensors', 'pacman' => 'lm-sensors', @@ -3475,6 +3479,13 @@ sub item_data { 'pacman' => 'xorg-xdpyinfo', 'rpm' => 'xorg-x11-utils (SUSE/Fedora?: xdpyinfo)', }, + 'xdriinfo' => { + 'info' => '-G (X) DRI driver (if missing, fallback to Xorg log)', + 'info-bsd' => '-G (X) DRI driver (if missing, fallback to Xorg log', + 'apt' => 'X11-utils', + 'pacman' => 'xorg-xdriinfo', + 'rpm' => 'xorg-x11-utils (SUSE/Fedora?: xdriinfo)', + }, 'xprop' => { 'info' => '-S (X) desktop data', 'info-bsd' => '-S (X) desktop data', @@ -5194,7 +5205,7 @@ sub get { if ($arg){ my $wl = 'bluetooth|compiler|cpu|dboot|dmidecode|elbrus|ipmi|logical|lspci|'; $wl .= 'partitions|pciconf|pcictl|pcidump|raid-btrfs|raid-hw|raid-lvm|'; - $wl .= 'raid-md|raid-soft|raid-zfs|sensors|swaymsg|sysctl|'; + $wl .= 'raid-md|raid-soft|raid-zfs|sensors|sensors-sys|swaymsg|sysctl|'; $wl .= 'uptime|usbconfig|usbdevs|vmstat|wl-info|wlr-randr|xorg-log|xrandr'; for (split(',',$arg)){ if ($_ =~ /\b($wl)\b/){ @@ -5220,7 +5231,7 @@ sub get { my ($opt,$arg) = @_; if ($arg){ my $wl = 'colors|cpuinfo|display|dmidecode|hddtemp|lsusb|man|meminfo|'; - $wl .= 'no-dig|no-doas|no-html-wan|no-sudo|pkg|rpm||usb-sys|'; + $wl .= 'no-dig|no-doas|no-html-wan|no-sudo|pkg|rpm|sensors-sys|usb-sys|'; $wl .= 'vmstat|wayland|wmctrl'; for (split(',',$arg)){ if ($_ =~ /\b($wl)\b/){ @@ -5340,6 +5351,8 @@ sub get { else { main::error_handler('bad-arg',$opt,$arg); }}, + 'sensors-sys' => sub { + $force{'sensors-sys'} = 1;}, 'sensors-use:s' => sub { my ($opt,$arg) = @_; if ($arg){ @@ -5790,7 +5803,8 @@ sub show_options { LMP version."], ['2', '-G', '', "GPU arch (AMD/Intel/Nvidia only); Specific vendor/product information (if relevant); PCI/USB ID of device; Direct rendering status - (in X); Screen number GPU is running on (Nvidia only)."], + (in X); Screen number GPU is running on (Nvidia only); device temp (Linux, + if found)."], ['2', '-i', '', "For IPv6, show additional scope addresses: Global, Site, Temporary, Unknown. See --limit for large counts of IP addresses."], ['2', '-I', '', "Default system GCC. With -xx, also shows other installed @@ -5802,7 +5816,8 @@ sub show_options { ['2', '-L', '', "For VG > LV, and other Devices, dm:"], ['2', '-m,--memory-modules', '', "Max memory module size (if available)."], ['2', '-N', '', "Specific vendor/product information (if relevant); - PCI/USB ID of device; Version/port(s)/driver version (if available)."], + PCI/USB ID of device; Version/port(s)/driver version (if available); device + temperature (Linux, if found)."], ['2', '-o,-p,-P', '', "Add mapped: name if partition mapped."], ['2', '-r', '', "Packages, see -Ix."], ['2', '-R', '', "md-raid: second RAID Info line with extra data: @@ -5828,7 +5843,6 @@ sub show_options { ['2', '-A', '', "Chip vendor:product ID for each audio device; PCIe speed, lanes (if found)."], ['2', '-B', '', "Serial number."], - ['2', '-C', '', "Add microarchitecture level."], ['2', '-D', '', "Disk transfer speed; NVMe lanes; Disk serial number; LVM volume group free space (if available); disk duid (some BSDs)."], ['2', '-E', '', "Chip vendor:product ID, LMP subversion; PCIe speed, lanes @@ -5911,11 +5925,12 @@ sub show_options { also sets --extra=3:"], ['2', '-A', '', "If available: list of alternate kernel modules/drivers for device(s); PCIe lanes-max: gen, speed, lanes (if relevant)."], - ['2', '-C', '', "If available: 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) if greater than 9; microcode format: hex."], + ['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) + if greater than 9; microcode format: hex."], ['2', '-d,-D', '', "If available: logical and physical block sizes; drive family; maj:min, USB drive specifics; SMART report."], ['2', '-E', '', "If available: in Report:, adds Info: line: acl-mtu, @@ -6023,9 +6038,9 @@ sub show_options { ['1', '', '--sensors-default', "Removes configuration item SENSORS_USE and SENSORS_EXCLUDE. Same as default behavior."], ['1', '', '--sensors-exclude', "[sensor[s] name, comma separated] Exclude - supplied sensor array[s] for -s output (lm-sensors, Linux only)."], + supplied sensor array[s] for -s output (lm-sensors, /sys. Linux only)."], ['1', '', '--sensors-use', "[sensor[s] name, comma separated] Use only - supplied sensor array[s] for -s output (lm-sensors, Linux only)."], + supplied sensor array[s] for -s output (lm-sensors, /sys. Linux only)."], ['1', '', '--sleep', "[0-x.x] Change CPU sleep time, in seconds, for -C (default:^$cpu_sleep). Allows system to catch up and show a more accurate CPU use. Example:^$self_name^-Cxxx^--sleep^0.15"], @@ -6787,6 +6802,7 @@ sub message { 'monitor-wayland' => 'no compositor data', 'note-check' => 'check', 'note-est' => 'est.', + 'note-not-reliable' => 'not reliable', 'nv-current' => "current (as of $id)", 'nv-legacy-active' => "legacy-active (EOL~$id)", 'nv-legacy-eol' => 'legacy (EOL)', @@ -6818,11 +6834,15 @@ sub message { 'root-suggested' => 'try sudo/root',# gdm only 'screen-wayland' => 'no compositor data', 'screen-xvesa' => 'no Xvesa data', - 'sensors-data-bsd' => "$id sensor data found but not usable.", - 'sensors-data-bsd-ok' => 'No sensor data found. Are sensors present?', - 'sensors-data-ipmi' => 'No ipmi sensor data found.', - 'sensors-data-linux' => 'No sensor data found. Is lm-sensors configured?', - 'sensors-ipmi-root' => 'Unable to run ipmi sensors. Root privileges required.', + 'sensor-data-bsd' => "$id sensor data found but not usable.", + 'sensor-data-bsd-ok' => 'No sensor data found. Are data sources present?', + 'sensor-data-bsd-unsupported' => 'Sensor data not available. Unsupported BSD variant.', + 'sensor-data-ipmi' => 'No ipmi sensor data found.', + 'sensor-data-ipmi-root' => 'Unable to run ipmi sensors. Root privileges required.', + 'sensors-data-linux' => 'No sensor data found. Missing /sys/class/hwmon, lm-sensors.', + 'sensor-data-lm-sensors' => 'No sensor data found. Is lm-sensors configured?', + 'sensor-data-sys' => 'No sensor data found in /sys/class/hwmon.', + 'sensor-data-sys-lm' => 'No sensor data found using /sys/class/hwmon or lm-sensors.', 'smartctl-command' => 'A mandatory SMART command failed. Various possible causes.', 'smartctl-open' => 'Unable to open device. Wrong device ID given?', 'smartctl-udma-crc' => 'Bad cable/connection?', @@ -7575,21 +7595,21 @@ sub sound_server_output { my ($program); my ($j,$num) = (0,0); foreach my $server (@{sound_server_data()}){ - next if $extra < 1 && (!$server->[2] || $server->[2] ne 'yes'); + next if $extra < 1 && (!$server->[3] || $server->[3] ne 'yes'); $j = scalar @$rows; - $server->[1] ||= 'N/A'; $server->[2] ||= 'N/A'; + $server->[3] ||= 'N/A'; push(@$rows, { - main::key($num++,1,1,'Sound Server') => $server->[0], - main::key($num++,0,2,'v') => $server->[1], - main::key($num++,0,2,'running') => $server->[2], + main::key($num++,1,1,$server->[0]) => $server->[1], + main::key($num++,0,2,'v') => $server->[2], + main::key($num++,0,2,'running') => $server->[3], }); } eval $end if $b_log; } sub sound_server_data { eval $start if $b_log; - my ($program,$running,$server,$version); + my ($program,$running,$server,$type,$version); my $servers = []; if (my $file = $system_files{'asound-version'}){ # avoid possible second line if compiled by user @@ -7598,55 +7618,61 @@ sub sound_server_data { $version = (split(/\s+/, $content))[-1]; $version =~ s/\.$//; # trim off period $server = 'ALSA'; + $type = 'Sound API'; $running = 'yes'; # not needed I think, if asound is there, it's running, but if that's # not correct, can use one of the info/list/stat tests for aplay # if (main::check_program('aplay') && main::grabber('aplay -l 2>/dev/null')){ # $running = 'yes'; # } - push(@$servers, [$server,$version,$running]); - ($running,$server,$version) = ('','',''); + push(@$servers, [$type,$server,$version,$running]); + ($running,$version) = ('',''); } # sndstat file may be removed in linux oss if (-e '/dev/sndstat' || ($program = main::check_program('ossinfo'))){ $server = 'OSS'; + $type = 'Sound API'; #$version = main::program_version('oss','\S',2); $version = (grep {/^hw.snd.version:/} @{$sysctl{'audio'}})[0] if $sysctl{'audio'}; $version = (split(/:\s*/,$version),1)[1] if $version; $version =~ s|/.*$|| if $version; # not a great test, but ok for now $running = (-e '/dev/sndstat') ? 'yes' : 'no?'; - push(@$servers, [$server,$version,$running]); - ($running,$server,$version) = ('','',''); + push(@$servers, [$type,$server,$version,$running]); + ($running,$version) = ('',''); } if ($program = main::check_program('sndiod')){ $server = 'sndio'; + $type = 'Sound Interface'; #$version = main::program_version('sndio','\S',2); $running = (grep {/sndiod/} @ps_cmd) ? 'yes': 'no'; - push(@$servers, [$server,$version,$running]); - ($running,$server,$version) = ('','',''); + push(@$servers, [$type,$server,$version,$running]); + ($running,$version) = ('',''); } if ($program = main::check_program('jackd')){ $server = 'JACK'; + $type = 'Sound Server'; $version = main::program_version($program,'^jackd',3,'--version',1); $running = (grep {/jackd/} @ps_cmd) ? 'yes':'no' ; - push(@$servers, [$server,$version,$running]); - ($running,$server,$version) = ('','',''); + push(@$servers, [$type,$server,$version,$running]); + ($running,$version) = ('',''); } # note: pactl info/list/stat could be used if ($program = main::check_program('pactl')){ $server = 'PulseAudio'; + $type = 'Sound Server'; $version = main::program_version($program,'^pactl',2,'--version',1); $running = (grep {m|/pulseaudiod?\b|} @ps_cmd) ? 'yes':'no' ; - push(@$servers, [$server,$version,$running]); - ($running,$server,$version) = ('','',''); + push(@$servers, [$type,$server,$version,$running]); + ($running,$version) = ('',''); } if ($program = main::check_program('pipewire')){ $server = 'PipeWire'; + $type = 'Sound Server'; $version = main::program_version($program,'^Compiled with libpipe',4,'--version',1); $running = (grep {/pipewire/} @ps_cmd) ? 'yes':'no' ; - push(@$servers, [$server,$version,$running]); - ($running,$server,$version) = ('','',''); + push(@$servers, [$type,$server,$version,$running]); + ($running,$version) = ('',''); } main::log_data('dump','sound servers: @$servers',$servers) if $b_log; print Data::Dumper::Dumper $servers if $dbg[26]; @@ -8744,8 +8770,11 @@ sub full_output { if ($b_admin && $cpu->{'gen'}){ $rows->[$j]{main::key($num++,0,3,'gen')} = $cpu->{'gen'}; } - if ($extra > 1 && $properties->{'arch-level'}){ - $rows->[$j]{main::key($num++,0,2,'level')} = $properties->{'arch-level'}; + if ($b_admin && $properties->{'arch-level'}){ + $rows->[$j]{main::key($num++,1,2,'level')} = $properties->{'arch-level'}[0]; + if ($properties->{'arch-level'}[1]){ + $rows->[$j]{main::key($num++,0,3,'note')} = $properties->{'arch-level'}[1]; + } } if ($b_admin){ if ($cpu->{'year'}){ @@ -10291,7 +10320,7 @@ sub cpu_properties { $bits_sys = ($cpu->{'flags'} =~ /\blm\b/) ? 64 : 32; } # must run after to make sure we have cpu bits - if (!%risc && $bits_sys && $bits_sys == 64 && $cpu->{'flags'}){ + if ($b_admin && !%risc && $bits_sys && $bits_sys == 64 && $cpu->{'flags'}){ $arch_level = cp_cpu_level( $cpu->{'flags'} ); @@ -10999,7 +11028,7 @@ sub cp_cpu_arch { $process = 'GF 12nm'; $year = '2018-21';} # used this but it didn't age well: ^(2[0123456789ABCDEF]| - elsif ($model =~ /^(31|47|60|68|71|90)$/){ + elsif ($model =~ /^(31|47|60|68|71|90|98|A.)$/){ $arch = 'Zen 2'; $gen = '3'; $process = 'TSMC n7 (7nm)'; # some consumer maybe GF 14nm @@ -11020,19 +11049,19 @@ sub cp_cpu_arch { elsif ($family eq '19'){ # ext model 6,7, but no base models yet # 10 engineering sample - if ($model =~ /^(10|[67][0-9A-F])$/){ + if ($model =~ /^(1.|6.|7.|A.)$/){ $arch = 'Zen 4'; $gen = '5'; $process = 'TSMC n5 (5nm)'; $year = '2022';} # double check 40, 44 - elsif ($model =~ /^(40|44)$/){ + elsif ($model =~ /^(4.)$/){ $arch = 'Zen 3+'; $gen = '4'; $process = 'TSMC n6 (7nm)'; $year = '2022';} - # 21, 50: step 0; - elsif ($model =~ /^(0|1|8|21|50)$/){ + # 21, 50: step 0; known: 21, 3x, 50 + elsif ($model =~ /^(0|1|8|2.|3.|5.)$/){ $arch = 'Zen 3'; $gen = '4'; $process = 'TSMC n7 (7nm)'; @@ -11094,72 +11123,73 @@ sub cp_cpu_arch { } } # note, to test uncoment $cpu{'type'} = Elbrus in proc/cpuinfo logic + # ExpLicit Basic Resources Utilization Scheduling elsif ($type eq 'elbrus'){ # E8CB if ($family eq '4'){ if ($model eq '1'){ - $arch = 'Elbrus'; - $process = ''; - $year = '';} + $arch = 'Elbrus 2000 (gen-1)'; + $process = 'Mikron 130nm'; + $year = '2005';} elsif ($model eq '2'){ - $arch = 'Elbrus-S'; - $process = ''; - $year = '';} + $arch = 'Elbrus-S (gen-2)'; + $process = 'Mikron 90nm'; + $year = '2010';} elsif ($model eq '3'){ - $arch = 'Elbrus-4C'; - $process = '65nm'; - $year = '';} + $arch = 'Elbrus-4C (gen-3)'; + $process = 'TSMC 65nm'; + $year = '2014';} elsif ($model eq '4'){ - $arch = 'Elbrus-2C+'; - $process = '90nm'; - $year = '';} + $arch = 'Elbrus-2C+ (gen-2)'; + $process = 'Mikron 90nm'; + $year = '2011';} elsif ($model eq '6'){ - $arch = 'Elbrus-2CM'; - $process = '90nm'; - $year = '';} + $arch = 'Elbrus-2CM (gen-2)'; + $note = $check; + $process = 'Mikron 90nm'; + $year = '2011 (?)';} elsif ($model eq '7'){ if ($stepping >= 2){ - $arch = 'Elbrus-8C1'; - $process = '28nm'; - $year = '';} + $arch = 'Elbrus-8C1 (gen-4)'; + $process = 'TSMC 28nm'; + $year = '2016';} else { - $arch = 'Elbrus-8C'; - $process = '28nm'; - $year = '';} + $arch = 'Elbrus-8C (gen-4)'; + $process = 'TSMC 28nm'; + $year = '2016';} } # note: stepping > 1 may be 8C1 elsif ($model eq '8'){ - $arch = 'Elbrus-1C+'; + $arch = 'Elbrus-1C+ (gen-4)'; $process = 'TSMC 40nm'; - $year = '';} + $year = '2016';} # 8C2 morphed out of E8CV, but the two were the same die elsif ($model eq '9'){ - $arch = 'Elbrus-8CV/8C2'; + $arch = 'Elbrus-8CV/8C2 (gen-4/5)'; $process = 'TSMC 28nm'; $note = $check; - $year = '';} + $year = '2016/2020';} elsif ($model eq 'A'){ - $arch = 'Elbrus-12C'; - $process = 'TSMC 16nm'; # guess - $year = '';} + $arch = 'Elbrus-12C (gen-6)'; + $process = 'TSMC 16nm'; + $year = '2021+';} elsif ($model eq 'B'){ - $arch = 'Elbrus-16C'; + $arch = 'Elbrus-16C (gen-6)'; $process = 'TSMC 16nm'; - $year = '';} + $year = '2021+';} elsif ($model eq 'C'){ - $arch = 'Elbrus-2C3'; + $arch = 'Elbrus-2C3 (gen-6)'; $process = 'TSMC 16nm'; - $year = '';} + $year = '2021+';} else { $arch = 'Elbrus-??';; - $year = ''; $note = $check; $year = '';} } elsif ($family eq '5'){ if ($model eq '9'){ - $arch = 'Elbrus-8C2'; + $arch = 'Elbrus-8C2 (gen-4)'; $process = 'TSMC 28nm'; - $year = '';} + $year = '2020';} else { $arch = 'Elbrus-??'; $note = $check; @@ -11168,17 +11198,21 @@ sub cp_cpu_arch { } elsif ($family eq '6'){ if ($model eq 'A'){ - $arch = 'Elbrus-12C'; - $process = 'TSMC 16nm'; # guess - $year = '';} + $arch = 'Elbrus-12C (gen-6)'; + $process = 'TSMC 16nm'; + $year = '2021+';} elsif ($model eq 'B'){ - $arch = 'Elbrus-16C'; + $arch = 'Elbrus-16C (gen-6)'; $process = 'TSMC 16nm'; - $year = '';} + $year = '2021+';} elsif ($model eq 'C'){ - $arch = 'Elbrus-2C3'; + $arch = 'Elbrus-2C3 (gen-6)'; $process = 'TSMC 16nm'; - $year = '';} + $year = '2021+';} + # elsif ($model eq '??'){ + # $arch = 'Elbrus-32C (gen-7)'; + # $process = '?? 7nm'; + # $year = '2025';} else { $arch = 'Elbrus-??'; $note = $check; @@ -11302,10 +11336,6 @@ sub cp_cpu_arch { $arch = 'M Tolapai'; # pentium M system on chip $process = 'Intel 90nm'; $year = '2008';} - elsif ($model =~ /^(1D)$/){ - $arch = 'Penryn'; - $process = 'Intel 45nm'; - $year = '2007-08';} elsif ($model =~ /^(17)$/){ $arch = 'Penryn'; # 17:A:Core 2,Celeron-wolfdale,yorkfield $process = 'Intel 45nm'; @@ -11319,6 +11349,10 @@ sub cp_cpu_arch { $arch = 'Bonnell'; $process = 'Intel 45nm'; $year = '2008-13';} # atom Bonnell? 27? + elsif ($model =~ /^(1D)$/){ + $arch = 'Penryn'; + $process = 'Intel 45nm'; + $year = '2007-08';} # 25 may be nahelem in a stepping, check. Stepping 2 is westmere elsif ($model =~ /^(25|2C|2F)$/){ $arch = 'Westmere'; # die shrink of nehalem @@ -11363,7 +11397,7 @@ sub cp_cpu_arch { $process = 'Intel 14nm'; $year = '2019';} elsif ($stepping >= 8){ - $arch = 'Cooper Lake'; + $arch = 'Cooper Lake'; # 55:A:14nm $process = 'Intel 14nm'; $year = '2020';} else { @@ -11382,12 +11416,12 @@ sub cp_cpu_arch { $arch = 'Skylake-S'; $process = 'Intel 14nm'; $year = '2015';} - elsif ($model =~ /^(66)$/){ + elsif ($model =~ /^(66|67)$/){ $arch = 'Cannon Lake'; $process = 'Intel 10nm'; $year = '2018';} # 6 are servers, 7 not - elsif ($model =~ /^(6A|6C|7D|7E)$/){ + elsif ($model =~ /^(6A|6C|7D|7E|9F)$/){ $arch = 'Ice Lake'; $process = 'Intel 10nm'; $year = '2019-21';} @@ -11399,10 +11433,22 @@ sub cp_cpu_arch { $arch = 'Knights Mill'; $process = 'Intel 14nm'; $year = '2017-19';} - elsif ($model =~ /^(8A|96|9C)$/){ - $arch = 'Tremont'; + elsif ($model =~ /^(86)$/){ + $arch = 'Tremont Snow Ridge'; # embedded $process = 'Intel 10nm'; - $year = '2019';} + $year = '2020';} + elsif ($model =~ /^(87)$/){ + $arch = 'Tremont Parker Ridge'; # embedded + $process = 'Intel 10nm'; + $year = '2022';} + elsif ($model =~ /^(8A)$/){ + $arch = 'Tremont Lakefield'; + $process = 'Intel 10nm'; + $year = '2020';} # ? + elsif ($model =~ /^(96)$/){ + $arch = 'Tremont Elkhart Lake'; + $process = 'Intel 10nm'; + $year = '2020';} # ? elsif ($model =~ /^(8C|8D)$/){ $arch = 'Tiger Lake'; $process = 'Intel 10nm'; @@ -11444,27 +11490,21 @@ sub cp_cpu_arch { $arch = 'Sapphire Rapids'; $process = 'Intel 7 (10nm ESF)'; $year = '2021+';} # server - elsif ($model =~ /^(97|9A)$/){ + elsif ($model =~ /^(97|9A|BE)$/){ $arch = 'Alder Lake'; # socket LG 1700 $process = 'Intel 7 (10nm ESF)'; $year = '2021+';} - ## IDS UNKNOWN, release late 2022 - # elsif ($model =~ /^()$/){ - # $arch = 'Raptor Lake'; # 13 gen, socket LG 1700,1800 - # $process = 'Intel 7 (10nm)'; - # $year = '2022+';} - # elsif ($model =~ /^()$/){ - # $arch = 'Meteor Lake'; # 14 gen - # $process = 'Intel 4';} - # Granite Rapids: Intel 3 (7nm) - # Arrow Lake - 15 gen + elsif ($model =~ /^(9A|9C)$/){ + $arch = 'Tremont Jasper Lake'; + $process = 'Intel 10nm'; + $year = '2021+';} # ? elsif ($model =~ /^(9E)$/){ if ($stepping == 9){ $arch = 'Kaby Lake'; $process = 'Intel 14nm'; $year = '2018';} elsif ($stepping >= 10 && $stepping <= 13){ - $arch = 'Coffee Lake'; + $arch = 'Coffee Lake'; # 9E:A,B,C,D $process = 'Intel 14nm'; $year = '2018';} else { @@ -11473,16 +11513,36 @@ sub cp_cpu_arch { $process = 'Intel 14nm'; $year = '2018';} } - elsif ($model =~ /^(A5)$/){ + elsif ($model =~ /^(A5|A6)$/){ $arch = 'Comet Lake'; # stepping 0-5 $process = 'Intel 14nm'; $year = '2020';} - elsif ($model =~ /^(A7)$/){ + elsif ($model =~ /^(A7|A8)$/){ $arch = 'Rocket Lake'; # stepping 1 $process = 'Intel 14nm'; $year = '2021+';} # More info: comet: shares family/model, need to find stepping numbers - # Coming: meteor lake; granite rapids; diamond rapids + # Coming: meteor lake; granite rapids; emerald rapids, diamond rapids + ## IDS UNKNOWN, release late 2022 + elsif ($model =~ /^(AA|AB|AC|B5)$/){ + $arch = 'Meteor Lake'; # 14 gen + $process = 'Intel 4 (7nm)'; # confirm + $year = '2023+';} + elsif ($model =~ /^(AD|AE)$/){ + $arch = 'Granite Rapids'; # ? + $process = 'Intel 3 (7nm+)'; # confirm + $year = '2024+';} + elsif ($model =~ /^(B6)$/){ + $arch = 'Grand Ridge'; # 14 gen + $process = 'Intel 4 (7nm)'; # confirm + $year = '2023+';} + elsif ($model =~ /^(B7|BA)$/){ + $arch = 'Raptor Lake'; # 13 gen, socket LG 1700,1800 + $process = 'Intel 7 (10nm)'; + $year = '2022+';} + # Granite Rapids: Intel 3 (7nm+) + # Arrow Lake - 15 gen, 20A (2nm), 2025 + # Lunar Lake - 16 gn, 18A (1.8nm), 2025 } # itanium 1 family 7 all recalled elsif ($family eq 'B'){ @@ -11502,7 +11562,12 @@ sub cp_cpu_arch { $process = 'Intel 180nm'; $year = '2000-01';} elsif ($model =~ /^(2)$/){ - $arch = 'Netburst Northwood'; + if ($stepping <= 4 || $stepping > 6){ + $arch = 'Netburst Northwood';} + elsif ($stepping == 5){ + $arch = 'Netburst Gallatin';} + else { + $arch = 'Netburst';} $process = 'Intel 130nm'; $year = '2002-03';} elsif ($model =~ /^(3)$/){ @@ -11510,6 +11575,7 @@ sub cp_cpu_arch { $process = 'Intel 90nm'; $year = '2004-06';} # 6? Nocona elsif ($model =~ /^(4)$/){ + # these are vague, and same stepping can have > 1 core names if ($stepping < 10){ $arch = 'Netburst Prescott'; # 4:1,9:prescott $process = 'Intel 90nm'; @@ -11520,7 +11586,7 @@ sub cp_cpu_arch { $year = '2005-06';} # 6? Nocona } elsif ($model =~ /^(6)$/){ - $arch = 'Netburst Presler'; + $arch = 'Netburst Presler'; # 6:2,4,5:presler $process = 'Intel 65nm'; $year = '2006';} else { @@ -11559,23 +11625,34 @@ sub cp_cpu_arch { # Only AMD/Intel 64 bit cpus sub cp_cpu_level { eval $start if $b_log; - my ($flags) = @_; - my $level; - if ($flags =~ /AVX512/i){ - $level = 'v4'; - } - # ~2015: Haswell and Excavator - elsif ($flags =~ /\b(AVX2|BMI[12]|F16C|FMA|LZCNT|MOVBE|OSXSAVE)\b/i){ - $level = 'v3'; - } - # !2009: Nehalem and Jaguar - elsif ($flags =~ /\b(CMPXCHG16B|[LS]AHF|POPCNT|SSS?E3|SSE4_[12])\b/i){ - $level = 'v2'; - } - # baseline: all x86_64 cpus - elsif ($flags =~ /\b(CX8|FPU|FXSR|MMX|OSFXSR|SCE|SSE2?)\b/i){ + 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; } @@ -13305,7 +13382,7 @@ sub set_disk_vendors { # HM320II HM320II HM ['(SAMSUNG|^(AWMB|[BC]DS20|[BC]WB|BJ[NT]|[BC]GND|CJN|CUT|[DG]3 Station|DUO\b|DUT|CKT|[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 # Android UMS Composite?U1 - ['(SanDisk|0781|^(ABLCD|AFGCE|D[AB]4|DX[1-9]|Extreme|Firebird|S[CD]\d{2}G|SD(S[S]?[ADQ]|SL\d+G|SU\d)|SDW[1-9]|SEM[1-9]|\d[STU]|U(3\b|1\d0))|Clip Sport|Cruzer|iXpand|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|Firebird|S[CD]\d{2}G|SD(S[S]?[ADQ]|SL\d+G|SU\d)|SDW[1-9]|SE\d{2}|SEM[1-9]|\d[STU]|U(3\b|1\d0))|Clip Sport|Cruzer|iXpand|SSD (Plus|U1[01]0) [1-9]|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 @@ -13330,7 +13407,7 @@ sub set_disk_vendors { ['^(DKR|HGST|Touro|54[15]0|7250|HC[CT]\d)','^HGST','HGST (Hitachi)',''], # HGST HUA ['^((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|[MV]B[0-6]|G[BJ]\d|DF\d|F[BK]|0-9]|MM\d{4}|PSS|XR\d{4}|c350|v\d{3}[bgorw]$|x\d{3}[w]$|VK0|HC[CPY]\d|EX9\d\d)','^HP','HP',''], + ['^(HP\b|[MV]B[0-6]|G[BJ]\d|DF\d|F[BK]|0-9]|MM\d{4}|PSS|XR\d{4}|c350|v\d{3}[bgorw]$|x\d{3}[w]$|VK0|HC[CPY]\d|EX9\d\d|VO0)','^HP','HP',''], ['^(Lexar|LSD|JumpDrive|JD\s?Firefly|LX\d|WorkFlow)','^Lexar','Lexar',''], # mmc-LEXAR_0xb016546c; JD Firefly; # these must come before maxtor because STM ['^STmagic','^STmagic','STmagic',''], @@ -13433,7 +13510,7 @@ sub set_disk_vendors { ['^Disain','^Disain','Disain',''], ['^(Disney|PIX[\s]?JR)','^Disney','Disney',''], ['^(Doggo|DQ-|Sendisk|Shenchu)','^(doggo|Sendisk(.?Shenchu)?|Shenchu(.?Sendisk)?)','Doggo (SENDISK/Shenchu)',''], - ['^(Dogfish|Shark)','^Dogfish(\s*Technology)?','Dogfish Technology',''], + ['^(Dogfish|M\.2 2242|Shark)','^Dogfish(\s*Technology)?','Dogfish Technology',''], ['^DragonDiamond','^DragonDiamond','DragonDiamond',''], ['^(DREVO\b|X1\s\d+[GT])','^DREVO','Drevo',''], ['^DSS','^DSS DAHUA','DSS DAHUA',''], @@ -13452,6 +13529,7 @@ sub set_disk_vendors { ['^Epson','^Epson','Epson',''], ['^(Etelcom|SSD051)','^Etelcom','Etelcom',''], ['^EURS','^EURS','EURS',''], + ['^eVAULT','^eVAULT','eVAULT',''], # NOTE: ESA3... may be IBM PCIe SAD card/drives ['^(EXCELSTOR|r technology)','^EXCELSTOR( TECHNO(LOGY)?)?','ExcelStor',''], ['^EYOTA','^EYOTA','EYOTA',''], @@ -13459,7 +13537,7 @@ sub set_disk_vendors { ['^EZLINK','^EZLINK','EZLINK',''], ['^Fantom','^Fantom( Drive[s]?)?','Fantom Drives',''], ['^Fanxiang','^Fanxiang','Fanxiang',''], - ['^Faspeed','^Faspeed','Faspeed',''], + ['^(Faspeed|K3[\s-])','^Faspeed','Faspeed',''], ['^FASTDISK','^FASTDISK','FASTDISK',''], ['^Festtive','^Festtive','Festtive',''], ['^FiiO','^FiiO','FiiO',''], @@ -13475,7 +13553,7 @@ sub set_disk_vendors { ['^(Garmin|Fenix|Nuvi|Zumo)','^Garmin','Garmin',''], ['^Geil','^Geil','Geil',''], ['^GelL','^GelL','GelL',''], # typo for Geil? GelL ZENITH R3 120GB - ['^(Generic|UY[67]|SLD)','^Generic','Generic',''], + ['^(Generic|G1J3|SCA128|SLD|UY[67])','^Generic','Generic',''], ['^(Genesis(\s?Logic)?|05e3)','(Genesis(\s?Logic)?|05e3)','Genesis Logic',''], ['^Geonix','^Geonix','Geonix',''], ['^Getrich','^Getrich','Getrich',''], @@ -13540,6 +13618,7 @@ sub set_disk_vendors { ['^Jingyi','^Jingyi','Jingyi',''], # NOTE: ITY2 120GB hard to find ['^JMicron','^JMicron(\s?Tech(nology)?)?','JMicron Tech',''], #JMicron H/W raid + ['^(Jual|RX7)','^Jual','Jual',''], ['^Kazuk','^Kazuk','Kazuk',''], ['(\bKDI\b|^OM3P)','\bKDI\b','KDI',''], ['^KLLISRE','^KLLISRE','KLLISRE',''], @@ -13579,7 +13658,7 @@ sub set_disk_vendors { ['^(LG\b|Xtick)','^LG','LG',''], ['(LITE[-\s]?ON[\s-]?IT)','LITE[-]?ON[\s-]?IT','LITE-ON IT',''], # LITEONIT_LSS-24L6G # PH6-CE240-L; CL1-3D256-Q11 NVMe LITEON 256GB - ['(LITE[-\s]?ON|^PH[1-9]|^DMT|^CV\d-|L(8[HT]|AT|C[HST]|JH|M[HST]|S[ST])-)','LITE[-]?ON','LITE-ON',''], + ['(LITE[-\s]?ON|^PH[1-9]|^DMT|^CV\d-|L(8[HT]|AT|C[HST]|JH|M[HST]|S[ST])-|^S900)','LITE[-]?ON','LITE-ON',''], ['^LONDISK','^LONDISK','LONDISK',''], ['^Longline','^Longline','Longline',''], ['^LuminouTek','^LuminouTek','LuminouTek',''], @@ -13627,7 +13706,7 @@ sub set_disk_vendors { ['^(MyDigitalSSD|BP[4X])','^MyDigitalSSD','MyDigitalSSD',''], # BP4 = BulletProof4 ['^(Myson)','^Myson([\s-]?Century)?([\s-]?Inc\.?)?','Myson Century',''], ['^(Neo\s*Forza|NFS\d)','^Neo\s*Forza','Neo Forza',''], - ['^(Netac|S535N)','^Netac','Netac',''], + ['^(Netac|OnlyDisk|S535N)','^Netac','Netac',''], ['^NFHK','^NFHK','NFHK',''], # NGFF is a type, like msata, sata ['^Nik','^Nikimi','Nikimi',''], @@ -13635,6 +13714,7 @@ sub set_disk_vendors { ['^ODYS','^ODYS','ODYS',''], ['^Olympus','^Olympus','Olympus',''], ['^Orico','^Orico','Orico',''], + ['^Ortial','^Ortial','Ortial',''], ['^OSC','^OSC\b','OSC',''], ['^oyunkey','^oyunkey','Oyunkey',''], ['^PALIT','PALIT','Palit',''], # ssd @@ -13673,6 +13753,7 @@ sub set_disk_vendors { ['^SAMSWEET','^SAMSWEET','Samsweet',''], ['^SandForce','^SandForce','SandForce',''], ['^Sannobel','^Sannobel','Sannobel',''], + ['^(Sansa|fuse\b)','^Sansa','Sansa',''], # SATADOM can be innodisk or supermirco: dom == disk on module # SATAFIRM is an ssd failure message ['^(Sea\s?Tech|Transformer)','^Sea\s?Tech','Sea Tech',''], @@ -13680,6 +13761,8 @@ sub set_disk_vendors { # DIAMOND_040_GB ['^(SILICON\s?MOTION|SM\d|090c)','^(SILICON\s?MOTION|090c)','Silicon Motion',''], ['(Silicon[\s-]?Power|^SP[CP]C|^Silicon|^Diamond|^HasTopSunlightpeed)','Silicon[\s-]?Power','Silicon Power',''], + # simple drive could also maybe be hgst + ['^(Simple\s?Tech|Simple[\s-]?Drive)','^Simple\s?Tech','SimpleTech',''], ['^SINTECHI?','^SINTECHI?','SinTech (adapter)',''], ['^SiS\b','^SiS','SiS',''], ['Smartbuy','\s?Smartbuy','Smartbuy',''], # SSD Smartbuy 60GB; mSata Smartbuy 3 @@ -14077,6 +14160,7 @@ sub device_output { return if !$devices{'graphics'}; my $rows = $_[0]; my ($j,$num) = (0,1); + my ($bus_id); set_monitors_sys() if !$monitor_ids && -e '/sys/class/drm'; foreach my $row (@{$devices{'graphics'}}){ $num = 1; @@ -14090,6 +14174,7 @@ sub device_output { # print "$row->[0] $row->[3]\n"; $j = scalar @$rows; my $device = main::trimmer($row->[4]); + ($bus_id) = (); $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){ @@ -14153,7 +14238,7 @@ sub device_output { } } if ($extra > 0){ - my $bus_id = (!$row->[2] && !$row->[3]) ? 'N/A' : "$row->[2].$row->[3]"; + $bus_id = (!$row->[2] && !$row->[3]) ? 'N/A' : "$row->[2].$row->[3]"; if ($extra > 1 && $bus_id ne 'N/A'){ main::get_pcie_data($bus_id,$j,$rows,\$num,'gpu'); } @@ -14169,6 +14254,12 @@ sub device_output { if ($extra > 2 && $row->[1]){ $rows->[$j]{main::key($num++,0,2,'class-ID')} = $row->[1]; } + if (!$bsd_type && $extra > 0 && $bus_id ne 'N/A' && $bus_id =~ /\.0$/){ + my $temp = main::get_device_temp($bus_id); + if ($temp){ + $rows->[$j]{main::key($num++,0,2,'temp')} = $temp . ' C'; + } + } # print "$row->[0]\n"; } eval $end if $b_log; @@ -14383,18 +14474,23 @@ sub display_output(){ # The only wayland setups with x drivers have xorg, transitional that is. if (@$x_drivers){ $rows->[$j]{main::key($num++,1,3,'X')} = ''; - my $driver = ($x_drivers->[0]) ? $x_drivers->[0] : 'N/A'; + my $driver = ($x_drivers->[0]) ? join(',',@{$x_drivers->[0]}) : 'N/A'; $rows->[$j]{main::key($num++,1,4,'loaded')} = $driver; if ($x_drivers->[1]){ - $rows->[$j]{main::key($num++,0,4,'unloaded')} = $x_drivers->[1]; + $rows->[$j]{main::key($num++,0,4,'unloaded')} = join(',',@{$x_drivers->[1]}); } if ($x_drivers->[2]){ - $rows->[$j]{main::key($num++,0,4,'failed')} = $x_drivers->[2]; + $rows->[$j]{main::key($num++,0,4,'failed')} = join(',',@{$x_drivers->[2]}); } if ($extra > 1 && $x_drivers->[3]){ - $rows->[$j]{main::key($num++,0,4,'alternate')} = $x_drivers->[3]; + $rows->[$j]{main::key($num++,0,4,'alternate')} = join(',',@{$x_drivers->[3]}); } } + if ($graphics{'dri-drivers'}){ + # note: if want to exclude if matches gpu/x driver, loop through and test. + # Here using all dri drivers found. + $rows->[$j]{main::key($num++,1,3,'dri')} = join(',',@{$graphics{'dri-drivers'}}); + } my $drivers; if (@$gpu_drivers){ $drivers = join(',',@$gpu_drivers); @@ -15342,7 +15438,7 @@ sub get_model_serial { # DISPLAY DATA X.org ## sub display_data_x { eval $start if $b_log; - my ($prog_xdpyinfo,$prog_xrandr); + my ($prog_xdpyinfo,$prog_xdriinfo,$prog_xrandr); if ($prog_xdpyinfo = main::check_program('xdpyinfo')){ xdpyinfo_data($prog_xdpyinfo); } @@ -15350,6 +15446,10 @@ sub display_data_x { if ($prog_xrandr = main::check_program('xrandr')){ xrandr_data($prog_xrandr); } + # if tool not installed, falls back to testing Xorg log file + if ($prog_xdriinfo = main::check_program('xdriinfo')){ + xdriinfo_data($prog_xdriinfo); + } if (!$graphics{'screens'}){ $graphics{'tty'} = tty_data(); } @@ -15368,6 +15468,40 @@ sub display_data_x { main::log_data('dump','$graphics{screens}',$graphics{'screens'}) if $b_log; eval $end if $b_log; } +sub xdriinfo_data { + eval $start if $b_log; + my $program = $_[0]; + my (%dri_drivers,$screen,$xdriinfo); + if (!$fake{'xdriinfo'}){ + $xdriinfo = main::grabber("$program $display_opt 2>/dev/null",'','strip','ref'); + } + else { + # $xdriinfo = main::reader("$ENV{HOME}/bin/scripts/inxi/data/xrandr/xrandr-test-1.txt",'strip','ref'); + } + foreach $screen (@$xdriinfo){ + if ($screen =~ /^Screen (\d+):\s+(\S+)/){ + $dri_drivers{$1} = $2 if $2 !~ /^not\b/; + } + } + if ($graphics{'screens'}){ + # assign to the screen if it's found + foreach $screen (@{$graphics{'screens'}}){ + if (defined $dri_drivers{$screen->{'screen'}} ){ + $screen->{'dri-driver'} = $dri_drivers{$screen->{'screen'}}; + } + } + } + # now the display drivers + foreach $screen (sort keys %dri_drivers){ + if (!$graphics{'dri-drivers'} || + !(grep {$dri_drivers{$screen} eq $_} @{$graphics{'dri-drivers'}})){ + push (@{$graphics{'dri-drivers'}},$dri_drivers{$screen}); + } + } + print 'x dri driver: ', Data::Dumper::Dumper \%dri_drivers if $dbg[17]; + main::log_data('dump','%dri_drivers',\%dri_drivers) if $b_log; + eval $end if $b_log; +} sub xdpyinfo_data { eval $start if $b_log; my ($program) = @_; @@ -15471,8 +15605,9 @@ sub xrandr_data { $xrandr = main::grabber("$program $display_opt 2>/dev/null",'','strip','ref'); } else { + $xrandr = main::reader("$ENV{HOME}/bin/scripts/inxi/data/xrandr/xrandr-4-displays-1.txt",'strip','ref'); # $xrandr = main::reader("$ENV{HOME}/bin/scripts/inxi/data/xrandr/xrandr-test-1.txt",'strip','ref'); - $xrandr = main::reader("$ENV{HOME}/bin/scripts/inxi/data/xrandr/xrandr-test-2.txt",'strip','ref'); + # $xrandr = main::reader("$ENV{HOME}/bin/scripts/inxi/data/xrandr/xrandr-test-2.txt",'strip','ref'); } # $graphics{'dimensions'} = (\@dimensions); # we get a bit more info from xrandr than xdpyinfo, but xrandr fails to handle @@ -15755,9 +15890,7 @@ sub gpu_drivers_sys { } sub display_drivers_x { eval $start if $b_log; - my ($driver,%drivers); my $driver_data = []; - my ($alternate,$failed,$loaded,$sep,$unloaded) = ('','','','',''); if (my $log = $system_files{'xorg-log'}){ if ($fake{'xorg-log'}){ # $log = "$ENV{HOME}/bin/scripts/inxi/data/xorg-logs/Xorg.0-voyager-serena.log"; @@ -15778,21 +15911,30 @@ sub display_drivers_x { sunbw2 suncg14 suncg3 suncg6 sunffb sunleo suntcx tdfx tga trident tseng unichrome v4l vboxvideo vesa vga via vmware vmwgfx voodoo)); $list = qr/$list/; # i only added perl 5.14, don't use + my ($b_use_dri,$dri,$driver,%drivers); + my ($alternate,$failed,$loaded,$unloaded); + my $pattern = 'Failed|Unload|Loading'; + # preferred source xdriinfo because it's current and accurate, but fallback here + if (!$graphics{'dri-drivers'}){ + $b_use_dri = 1; + $pattern .= '|DRI driver:'; + } + $pattern = qr/$pattern/; # it's much cheaper to grab the simple pattern match then do the expensive one # in the main loop. # @xorg = grep {/Failed|Unload|Loading/} @xorg; - foreach (@$xorg){ - next if !/Failed|Unload|Loading/; + foreach my $line (@$xorg){ + next if $line !~ /$pattern/i; # print "$_\n"; # note that in file names, driver is always lower case - if (/\sLoading.*($list)_drv\.so$/i){ + if ($line =~ /\sLoading.*($list)_drv\.so$/i){ $driver=lc($1); # we get all the actually loaded drivers first, we will use this to compare the # failed/unloaded, which have not always actually been truly loaded $drivers{$driver}='loaded'; } # openbsd uses UnloadModule: - elsif (/(Unloading\s|UnloadModule).*\"?($list)(_drv\.so)?\"?$/i){ + elsif ($line =~ /(Unloading\s|UnloadModule).*\"?($list)(_drv\.so)?\"?$/i){ $driver=lc($2); # we get all the actually loaded drivers first, we will use this to compare the # failed/unloaded, which have not always actually been truly loaded @@ -15808,14 +15950,14 @@ sub display_drivers_x { # (II) Unloading nouveau # (II) Failed to load module "nouveau" (already loaded, 0) # (II) LoadModule: "modesetting" - elsif (/Failed.*($list)\"?.*$/i){ + elsif ($line =~ /Failed.*($list)\"?.*$/i){ # Set driver to lower case because sometimes it will show as # RADEON or NVIDIA in the actual x start $driver=lc($1); # we need to make sure that the driver has already been truly loaded, # not just discussed if (exists $drivers{$driver} && $drivers{$driver} ne 'alternate'){ - if ($_ !~ /\(already loaded/){ + if ($line !~ /\(already loaded/){ $drivers{$driver}='failed'; } # reset the previous line's 'unloaded' to 'loaded' as well @@ -15823,28 +15965,30 @@ sub display_drivers_x { $drivers{$driver}='loaded'; } } - elsif ($_ =~ /module does not exist/){ + elsif ($line =~ /module does not exist/){ $drivers{$driver}='alternate'; } } + elsif ($b_use_dri && $line =~ /DRI driver:\s*(\S+)/i){ + $dri = $1; + if (!$graphics{'dri-drivers'} || + !(grep {$dri eq $_} @{$graphics{'dri-drivers'}})){ + push(@{$graphics{'dri-drivers'}},$dri); + } + } } - my $sep = ''; foreach (sort keys %drivers){ if ($drivers{$_} eq 'loaded'){ - $sep = ($loaded) ? ',' : ''; - $loaded .= $sep . $_; + push(@$loaded,$_); } elsif ($drivers{$_} eq 'unloaded'){ - $sep = ($unloaded) ? ',' : ''; - $unloaded .= $sep . $_; + push(@$unloaded,$_); } elsif ($drivers{$_} eq 'failed'){ - $sep = ($failed) ? ',' : ''; - $failed .= $sep . $_; + push(@$failed,$_); } elsif ($drivers{$_} eq 'alternate'){ - $sep = ($alternate) ? ',' : ''; - $alternate .= $sep . $_; + push(@$alternate,$_); } } if ($loaded || $unloaded || $failed || $alternate){ @@ -16195,7 +16339,8 @@ sub set_intel_data { 'years' => '2020-21', }, {'arch' => 'Gen-12.2', - 'ids' => '4626|4628|4682|4688|4690|4692|4693|46a3|46a6|46a8|46aa|46b3|46c3', + 'ids' => '4626|4628|4682|4688|468a|468b|4690|4692|4693|46a3|46a6|46a8|46aa|' . + '46b3|46c3', 'code' => '', 'process' => 'Intel 10nm', 'years' => '2021-22+', @@ -16486,7 +16631,7 @@ sub set_nv_data { {'arch' => 'Hopper', 'ids' => '', 'code' => 'GH1xx', - 'pattern' => 'G?H[12]\d{2}', + 'pattern' => '\bG?H[12]\d{2}', 'process' => 'TSMC n4 (5nm)', 'series' => '515.xx+', 'status' => $status_current, @@ -16495,8 +16640,8 @@ sub set_nv_data { {'arch' => 'Lovelace', 'ids' => '', 'code' => 'AD1xx', - 'pattern' => 'G?L\d{1,4}|RTX 40\d{2}', - 'process' => 'TSMC n5 (5nm)', + 'pattern' => '\bG?L\d{1,4}|\bAD1\d{2}|RTX [6-8]0\d{2}', + 'process' => 'TSMC n4 (5nm)', 'series' => '515.xx+', 'status' => $status_current, 'years' => '2022-23+', @@ -16528,8 +16673,8 @@ sub get_gpu_data { eval $start if $b_log; my ($gpu,$p_id,$name) = @_; my ($info); - # Reverse, newer will be more common geneerally than older, so save some cpu! - # This means for amd/intel/nvidia current, the regex name search runs FIRST! + # Don't use reverse because if product ID is matched, we want that, not a looser + # regex match. Tried with reverse and led to false matches. foreach my $item (reverse @$gpu){ next if !$item->{'ids'} && (!$item->{'pattern'} || !$name); if (($item->{'ids'} && $p_id =~ /^($item->{'ids'})$/) || @@ -16718,8 +16863,9 @@ sub advanced_monitor_data { push(@vert,$monitors->{$key}{'pos-y'}); } } - @horiz = sort(@horiz); - @vert = sort(@vert); + # we need NUMERIC sort, because positions can be less than 1000! + @horiz = sort {$a <=> $b} @horiz; + @vert =sort {$a <=> $b} @vert; my ($h,$v) = (scalar(@horiz),scalar(@vert)); # print Data::Dumper::Dumper \@horiz; # print Data::Dumper::Dumper \@vert; @@ -18272,9 +18418,12 @@ sub device_output { my $bus_id = 'N/A'; # note: for arm/mips we want to see the single item bus id, why not? # note: we can have bus id: 0002 / 0 which is valid, but 0 / 0 is invalid - if (defined $row->[2] && $row->[2] ne '0' && defined $row->[3]){$bus_id = "$row->[2].$row->[3]"} - elsif (defined $row->[2] && $row->[2] ne '0'){$bus_id = $row->[2]} - elsif (defined $row->[3] && $row->[3] ne '0'){$bus_id = $row->[3]} + if (defined $row->[2] && $row->[2] ne '0' && defined $row->[3]){ + $bus_id = "$row->[2].$row->[3]"} + elsif (defined $row->[2] && $row->[2] ne '0'){ + $bus_id = $row->[2]} + elsif (defined $row->[3] && $row->[3] ne '0'){ + $bus_id = $row->[3]} if ($extra > 0){ if ($row->[9] && !$bsd_type){ my $version = main::get_module_version($row->[9]); @@ -18290,7 +18439,9 @@ sub device_output { main::get_pcie_data($bus_id,$j,$rows,\$num); } # as far as I know, wifi has no port, but in case it does in future, use it - $rows->[$j]{main::key($num++,0,2,'port')} = $row->[8] if (!$b_wifi || ($b_wifi && $row->[8] ne 'N/A')); + if (!$b_wifi || ($b_wifi && $row->[8] ne 'N/A')){ + $rows->[$j]{main::key($num++,0,2,'port')} = $row->[8]; + } $rows->[$j]{main::key($num++,0,2,'bus-ID')} = $bus_id; } if ($extra > 1){ @@ -18299,6 +18450,12 @@ sub device_output { if ($extra > 2 && $row->[1]){ $rows->[$j]{main::key($num++,0,2,'class-ID')} = $row->[1]; } + if (!$bsd_type && $extra > 0 && $bus_id ne 'N/A' && $bus_id =~ /\.0$/){ + my $temp = main::get_device_temp($bus_id); + if ($temp){ + $rows->[$j]{main::key($num++,0,2,'temp')} = $temp . ' C'; + } + } if ($show{'network-advanced'}){ my @data; if (!$bsd_type){ @@ -23022,10 +23179,12 @@ sub file_path { package SensorItem; my $gpu_data = []; my $sensors_raw = {}; +my $max_fan = 15000; sub get { eval $start if $b_log; - my ($b_data,$key1,$program,$val1,$sensors); - my ($num,$rows) = (0,[]); + my ($b_data,$b_ipmi,$b_no_lm,$b_no_sys); + my ($message_type,$program,$val1,$sensors); + my ($key1,$num,$rows) = ('Message',0,[]); my $source = 'sensors'; # will trip some type output if ipmi + another type # we're allowing 1 or 2 ipmi tools, first the gnu one, then the # almost certain to be present in BSDs @@ -23036,53 +23195,98 @@ sub get { $sensors = ipmi_data($program); $b_data = sensors_output($rows,'ipmi',$sensors); if (!$b_data){ - $key1 = 'Message'; - $val1 = main::message('sensors-data-ipmi'); - # $val1 = main::message('dev'); - push(@$rows,{main::key($num++,0,1,$key1) => $val1}); + $val1 = main::message('sensor-data-ipmi'); + push(@$rows,{ + main::key($num++,1,1,'Src') => 'ipmi', + main::key($num++,0,1,$key1) => $val1, + }); } - $source = 'lm-sensors'; # trips per sensor type output } else { $key1 = 'Permissions'; - $val1 = main::message('sensors-ipmi-root'); - push(@$rows,{main::key($num++,0,1,$key1) => $val1}); + $val1 = main::message('sensor-data-ipmi-root'); + push(@$rows,{ + main::key($num++,1,1,'Src') => 'ipmi', + main::key($num++,0,2,$key1) => $val1, + }); } + $b_ipmi = 1; } - if ($sysctl{'sensor'}){ - $sensors = sysctl_data(); - $b_data = sensors_output($rows,'sysctl-sensors',$sensors); - if (!$b_data){ - $key1 = 'Message'; - $val1 = main::message('sensors-data-bsd',$uname[0]); - push(@$rows,{main::key($num++,0,1,$key1) => $val1}); + $b_data = 0; + if ($bsd_type){ + if ($sysctl{'sensor'}){ + $sensors = sysctl_data(); + $source = 'sysctl' if $b_ipmi; + $b_data = sensors_output($rows,$source,$sensors); + if (!$b_data){ + $source = 'sysctl'; + $val1 = main::message('sensor-data-bsd',$uname[0]); + } + } + else { + if ($bsd_type =~ /^(free|open)bsd/){ + $source = 'sysctl'; + $val1 = main::message('sensor-data-bsd-ok'); + } + else { + $source = 'N/A'; + $val1 = main::message('sensor-data-bsd-unsupported'); + } } } else { - if (!$fake{'sensors'} && $alerts{'sensors'}->{'action'} ne 'use'){ + if (!$force{'sensors-sys'} && + ($fake{'sensors'} || $alerts{'sensors'}->{'action'} eq 'use')){ + load_lm_sensors(); + $sensors = linux_sensors_data(); + $source = 'lm-sensors' if $b_ipmi; # trips per sensor type output + $b_data = sensors_output($rows,$source,$sensors); # print "here 1\n"; - if ($bsd_type && $bsd_type =~ /^(free|open)bsd/){ - $key1 = 'Message'; - $val1 = main::message('sensors-data-bsd-ok'); - } - else { - $key1 = $alerts{'sensors'}->{'action'}; - $val1 = $alerts{'sensors'}->{'message'}; - $key1 = ucfirst($key1); - } - push(@$rows,{main::key($num++,0,1,$key1) => $val1,}); + $b_no_lm = 1 if !$b_data; } - else { - $sensors = lm_sensors_data(); + # given recency of full /sys data, we want to prefer lm-sensors for a long time + # and use /sys as a fallback. This will handle servers, which often do not + # have lm-sensors installed, but do have /sys hwmon data. + if (!$b_data && -d '/sys/class/hwmon'){ + load_sys_data(); + $sensors = linux_sensors_data(); + $source = '/sys'; # trips per sensor type output $b_data = sensors_output($rows,$source,$sensors); # print "here 2\n"; - if (!$b_data){ - $key1 = 'Message'; - $val1 = main::message('sensors-data-linux'); - push(@$rows,{main::key($num++,0,1,$key1) => $val1}); - } - + $b_no_sys = 1 if !$b_data; } + if (!$b_data){ + if ($b_no_lm || $b_no_sys){ + if ($b_no_lm && $b_no_sys){ + $source = 'lm-sensors+/sys'; + $val1 = main::message('sensor-data-sys-lm'); + } + elsif ($b_no_lm){ + $source = 'lm-sensors'; + $val1 = main::message('sensor-data-lm-sensors'); + } + else { + $val1 = main::message('sensor-data-sys'); + } + } + elsif (!$fake{'sensors'} && $alerts{'sensors'}->{'action'} ne 'use'){ + # print "here 3\n"; + $source = 'lm-sensors'; + $key1 = $alerts{'sensors'}->{'action'}; + $key1 = ucfirst($key1); + $val1 = $alerts{'sensors'}->{'message'}; + } + else { + $source = 'N/A'; + $val1 = main::message('sensors-data-linux'); + } + } + } + if (!$b_data){ + push(@$rows,{ + main::key($num++,1,1,'Src') => $source, + main::key($num++,0,2,$key1) => $val1, + }); } eval $end if $b_log; return $rows; @@ -23095,129 +23299,128 @@ sub sensors_output { my $num = 0; my $j = scalar @$rows; if (!$loaded{'gpu-data'} && - ($source eq 'sensors' || $source eq 'lm-sensors')){ + ($source eq 'sensors' || $source eq 'lm-sensors' || $source eq '/sys')){ gpu_sensor_data(); } # gpu sensors data might be present even if standard sensors data wasn't return if !%$sensors && !@$gpu_data; - $b_result = 1; ## need t trip data found conditions + $b_result = 1; ## need to trip data found conditions my $temp_unit = (defined $sensors->{'temp-unit'}) ? " $sensors->{'temp-unit'}": ''; my $cpu_temp = (defined $sensors->{'cpu-temp'}) ? $sensors->{'cpu-temp'} . $temp_unit: 'N/A'; my $mobo_temp = (defined $sensors->{'mobo-temp'}) ? $sensors->{'mobo-temp'} . $temp_unit: 'N/A'; - my $cpu1_key = ($sensors->{'cpu2-temp'}) ? 'cpu-1': 'cpu' ; - my $data_source = ($source eq 'ipmi' || $source eq 'lm-sensors') ? $source : ''; - push(@$rows, { - main::key($num++,1,1,'System Temperatures') => $data_source, - main::key($num++,0,2,$cpu1_key) => $cpu_temp, - }); + my $cpu1_key = ($sensors->{'cpu2-temp'}) ? 'cpu-1': 'cpu'; + my ($l1,$l2,$l3) = (1,2,3); + if ($source ne 'sensors'){ + $rows->[$j]{main::key($num++,1,1,'Src')} = $source; + ($l1,$l2,$l3) = (2,3,4); + } + $rows->[$j]{main::key($num++,1,$l1,'System Temperatures')} = ''; + $rows->[$j]{main::key($num++,0,$l2,$cpu1_key)} = $cpu_temp; if ($sensors->{'cpu2-temp'}){ - $rows->[$j]{main::key($num++,0,2,'cpu-2')} = $sensors->{'cpu2-temp'} . $temp_unit; + $rows->[$j]{main::key($num++,0,$l2,'cpu-2')} = $sensors->{'cpu2-temp'} . $temp_unit; } if ($sensors->{'cpu3-temp'}){ - $rows->[$j]{main::key($num++,0,2,'cpu-3')} = $sensors->{'cpu3-temp'} . $temp_unit; + $rows->[$j]{main::key($num++,0,$l2,'cpu-3')} = $sensors->{'cpu3-temp'} . $temp_unit; } if ($sensors->{'cpu4-temp'}){ - $rows->[$j]{main::key($num++,0,2,'cpu-4')} = $sensors->{'cpu4-temp'} . $temp_unit; + $rows->[$j]{main::key($num++,0,$l2,'cpu-4')} = $sensors->{'cpu4-temp'} . $temp_unit; } if (defined $sensors->{'pch-temp'}){ my $pch_temp = $sensors->{'pch-temp'} . $temp_unit; - $rows->[$j]{main::key($num++,0,2,'pch')} = $pch_temp; + $rows->[$j]{main::key($num++,0,$l2,'pch')} = $pch_temp; } - $rows->[$j]{main::key($num++,0,2,'mobo')} = $mobo_temp; + $rows->[$j]{main::key($num++,0,$l2,'mobo')} = $mobo_temp; if (defined $sensors->{'sodimm-temp'}){ my $sodimm_temp = $sensors->{'sodimm-temp'} . $temp_unit; - $rows->[$j]{main::key($num++,0,2,'sodimm')} = $sodimm_temp; + $rows->[$j]{main::key($num++,0,$l2,'sodimm')} = $sodimm_temp; } if (defined $sensors->{'psu-temp'}){ my $psu_temp = $sensors->{'psu-temp'} . $temp_unit; - $rows->[$j]{main::key($num++,0,2,'psu')} = $psu_temp; + $rows->[$j]{main::key($num++,0,$l2,'psu')} = $psu_temp; } if (defined $sensors->{'ambient-temp'}){ my $ambient_temp = $sensors->{'ambient-temp'} . $temp_unit; - $rows->[$j]{main::key($num++,0,2,'ambient')} = $ambient_temp; + $rows->[$j]{main::key($num++,0,$l2,'ambient')} = $ambient_temp; } if (scalar @$gpu_data == 1 && defined $gpu_data->[0]{'temp'}){ my $gpu_temp = $gpu_data->[0]{'temp'}; my $gpu_type = $gpu_data->[0]{'type'}; my $gpu_unit = (defined $gpu_data->[0]{'temp-unit'} && $gpu_temp) ? " $gpu_data->[0]{'temp-unit'}" : ' C'; - $rows->[$j]{main::key($num++,1,2,'gpu')} = $gpu_type; - $rows->[$j]{main::key($num++,0,3,'temp')} = $gpu_temp . $gpu_unit; + $rows->[$j]{main::key($num++,1,$l2,'gpu')} = $gpu_type; + $rows->[$j]{main::key($num++,0,$l3,'temp')} = $gpu_temp . $gpu_unit; if ($extra > 1 && $gpu_data->[0]{'temp-mem'}){ - $rows->[$j]{main::key($num++,0,3,'mem')} = $gpu_data->[0]{'temp-mem'} . $gpu_unit; + $rows->[$j]{main::key($num++,0,$l3,'mem')} = $gpu_data->[0]{'temp-mem'} . $gpu_unit; } } $j = scalar @$rows; @fan_main = @{$sensors->{'fan-main'}} if $sensors->{'fan-main'}; @fan_default = @{$sensors->{'fan-default'}} if $sensors->{'fan-default'}; - my $fan_def = ($data_source) ? $data_source : ''; - if (!@fan_main && !@fan_default){ - $fan_def = ($fan_def) ? "$data_source N/A" : 'N/A'; - } - $rows->[$j]{main::key($num++,1,1,'Fan Speeds (RPM)')} = $fan_def; + my $fan_def = (!@fan_main && !@fan_default) ? 'N/A' : ''; + $rows->[$j]{main::key($num++,1,$l1,'Fan Speeds (RPM)')} = $fan_def; my $b_cpu = 0; for (my $i = 0; $i < scalar @fan_main; $i++){ next if $i == 0;# starts at 1, not 0 if (defined $fan_main[$i]){ if ($i == 1 || ($i == 2 && !$b_cpu)){ - $rows->[$j]{main::key($num++,0,2,'cpu')} = $fan_main[$i]; + $rows->[$j]{main::key($num++,0,$l2,'cpu')} = $fan_main[$i]; $b_cpu = 1; } elsif ($i == 2 && $b_cpu){ - $rows->[$j]{main::key($num++,0,2,'mobo')} = $fan_main[$i]; + $rows->[$j]{main::key($num++,0,$l2,'mobo')} = $fan_main[$i]; } elsif ($i == 3){ - $rows->[$j]{main::key($num++,0,2,'psu')} = $fan_main[$i]; + $rows->[$j]{main::key($num++,0,$l2,'psu')} = $fan_main[$i]; } elsif ($i == 4){ - $rows->[$j]{main::key($num++,0,2,'sodimm')} = $fan_main[$i]; + $rows->[$j]{main::key($num++,0,$l2,'sodimm')} = $fan_main[$i]; } elsif ($i > 4){ $fan_number = $i - 4; - $rows->[$j]{main::key($num++,0,2,"case-$fan_number")} = $fan_main[$i]; + $rows->[$j]{main::key($num++,0,$l2,"case-$fan_number")} = $fan_main[$i]; } } } for (my $i = 0; $i < scalar @fan_default; $i++){ next if $i == 0;# starts at 1, not 0 if (defined $fan_default[$i]){ - $rows->[$j]{main::key($num++,0,2,"fan-$i")} = $fan_default[$i]; + $rows->[$j]{main::key($num++,0,$l2,"fan-$i")} = $fan_default[$i]; } } - $rows->[$j]{main::key($num++,0,2,'psu')} = $sensors->{'fan-psu'} if defined $sensors->{'fan-psu'}; - $rows->[$j]{main::key($num++,0,2,'psu-1')} = $sensors->{'fan-psu1'} if defined $sensors->{'fan-psu1'}; - $rows->[$j]{main::key($num++,0,2,'psu-2')} = $sensors->{'fan-psu2'} if defined $sensors->{'fan-psu2'}; + $rows->[$j]{main::key($num++,0,$l2,'psu')} = $sensors->{'fan-psu'} if defined $sensors->{'fan-psu'}; + $rows->[$j]{main::key($num++,0,$l2,'psu-1')} = $sensors->{'fan-psu1'} if defined $sensors->{'fan-psu1'}; + $rows->[$j]{main::key($num++,0,$l2,'psu-2')} = $sensors->{'fan-psu2'} if defined $sensors->{'fan-psu2'}; # note: so far, only nvidia-settings returns speed, and that's in percent if (scalar @$gpu_data == 1 && defined $gpu_data->[0]{'fan-speed'}){ my $gpu_fan = $gpu_data->[0]{'fan-speed'} . $gpu_data->[0]{'speed-unit'}; my $gpu_type = $gpu_data->[0]{'type'}; - $rows->[$j]{main::key($num++,1,2,'gpu')} = $gpu_type; - $rows->[$j]{main::key($num++,0,3,'fan')} = $gpu_fan; + $rows->[$j]{main::key($num++,1,$l2,'gpu')} = $gpu_type; + $rows->[$j]{main::key($num++,0,$l3,'fan')} = $gpu_fan; } if (scalar @$gpu_data > 1){ $j = scalar @$rows; - $rows->[$j]{main::key($num++,1,1,'GPU')} = ''; + $rows->[$j]{main::key($num++,1,$l1,'GPU')} = ''; my $gpu_unit = (defined $gpu_data->[0]{'temp-unit'}) ? " $gpu_data->[0]{'temp-unit'}" : ' C'; foreach my $info (@$gpu_data){ # speed unit is either '' or % my $gpu_fan = (defined $info->{'fan-speed'}) ? $info->{'fan-speed'} . $info->{'speed-unit'}: undef; my $gpu_type = $info->{'type'}; my $gpu_temp = (defined $info->{'temp'}) ? $info->{'temp'} . $gpu_unit: 'N/A'; - $rows->[$j]{main::key($num++,1,2,'device')} = $gpu_type; + $rows->[$j]{main::key($num++,1,$l2,'device')} = $gpu_type; if (defined $info->{'screen'}){ - $rows->[$j]{main::key($num++,0,3,'screen')} = $info->{'screen'}; + $rows->[$j]{main::key($num++,0,$l3,'screen')} = $info->{'screen'}; } - $rows->[$j]{main::key($num++,0,3,'temp')} = $gpu_temp; + $rows->[$j]{main::key($num++,0,$l3,'temp')} = $gpu_temp; if ($extra > 1 && $info->{'temp-mem'}){ - $rows->[$j]{main::key($num++,0,3,'mem')} = $info->{'temp-mem'} . $gpu_unit; + $rows->[$j]{main::key($num++,0,$l3,'mem')} = $info->{'temp-mem'} . $gpu_unit; } if (defined $gpu_fan){ - $rows->[$j]{main::key($num++,0,3,'fan')} = $gpu_fan; + $rows->[$j]{main::key($num++,0,$l3,'fan')} = $gpu_fan; } if ($extra > 2 && $info->{'watts'}){ - $rows->[$j]{main::key($num++,0,3,'watts')} = $info->{'watts'}; + $rows->[$j]{main::key($num++,0,$l3,'watts')} = $info->{'watts'}; } - if ($extra > 2 && $info->{'mvolts'}){ - $rows->[$j]{main::key($num++,0,3,'mV')} = $info->{'mvolts'}; + if ($extra > 2 && $info->{'volts-gpu'}){ + $rows->[$j]{main::key($num++,0,$l3,$info->{'volts-gpu'}[1])} = $info->{'volts-gpu'}[0]; } } } @@ -23229,24 +23432,36 @@ sub sensors_output { $sensors->{'volts-5'} ||= 'N/A'; $sensors->{'volts-3.3'} ||= 'N/A'; $sensors->{'volts-vbat'} ||= 'N/A'; - $rows->[$j]{main::key($num++,1,1,'Power')} = $data_source; - $rows->[$j]{main::key($num++,0,2,'12v')} = $sensors->{'volts-12'}; - $rows->[$j]{main::key($num++,0,2,'5v')} = $sensors->{'volts-5'}; - $rows->[$j]{main::key($num++,0,2,'3.3v')} = $sensors->{'volts-3.3'}; - $rows->[$j]{main::key($num++,0,2,'vbat')} = $sensors->{'volts-vbat'}; + $rows->[$j]{main::key($num++,1,$l1,'Power')} = ''; + $rows->[$j]{main::key($num++,0,$l2,'12v')} = $sensors->{'volts-12'}; + $rows->[$j]{main::key($num++,0,$l2,'5v')} = $sensors->{'volts-5'}; + $rows->[$j]{main::key($num++,0,$l2,'3.3v')} = $sensors->{'volts-3.3'}; + $rows->[$j]{main::key($num++,0,$l2,'vbat')} = $sensors->{'volts-vbat'}; if ($extra > 1 && $source eq 'ipmi'){ $sensors->{'volts-dimm-p1'} ||= 'N/A'; $sensors->{'volts-dimm-p2'} ||= 'N/A'; - $rows->[$j]{main::key($num++,0,2,'dimm-p1')} = $sensors->{'volts-dimm-p1'} if $sensors->{'volts-dimm-p1'}; - $rows->[$j]{main::key($num++,0,2,'dimm-p2')} = $sensors->{'volts-dimm-p2'} if $sensors->{'volts-dimm-p2'}; - $rows->[$j]{main::key($num++,0,2,'soc-p1')} = $sensors->{'volts-soc-p1'} if $sensors->{'volts-soc-p1'}; - $rows->[$j]{main::key($num++,0,2,'soc-p2')} = $sensors->{'volts-soc-p2'} if $sensors->{'volts-soc-p2'}; + if ($sensors->{'volts-dimm-p1'}){ + $rows->[$j]{main::key($num++,0,$l2,'dimm-p1')} = $sensors->{'volts-dimm-p1'}; + } + if ($sensors->{'volts-dimm-p2'}){ + $rows->[$j]{main::key($num++,0,$l2,'dimm-p2')} = $sensors->{'volts-dimm-p2'}; + } + if ($sensors->{'volts-soc-p1'}){ + $rows->[$j]{main::key($num++,0,$l2,'soc-p1')} = $sensors->{'volts-soc-p1'}; + } + if ($sensors->{'volts-soc-p2'}){ + $rows->[$j]{main::key($num++,0,$l2,'soc-p2')} = $sensors->{'volts-soc-p2'}; + } } if (scalar @$gpu_data == 1 && $extra > 2 && - ($gpu_data->[0]{'watts'} || $gpu_data->[0]{'mvolts'})){ - $rows->[$j]{main::key($num++,1,2,'gpu')} = $gpu_data->[0]{'type'}; - $rows->[$j]{main::key($num++,0,3,'watts')} = $gpu_data->[0]{'watts'} if $gpu_data->[0]{'watts'}; - $rows->[$j]{main::key($num++,0,3,'mV')} = $gpu_data->[0]{'mvolts'} if $gpu_data->[0]{'mvolts'}; + ($gpu_data->[0]{'watts'} || $gpu_data->[0]{'volts-gpu'})){ + $rows->[$j]{main::key($num++,1,$l2,'gpu')} = $gpu_data->[0]{'type'}; + if ($gpu_data->[0]{'watts'}){ + $rows->[$j]{main::key($num++,0,$l3,'watts')} = $gpu_data->[0]{'watts'}; + } + if ($gpu_data->[0]{'volts-gpu'}){ + $rows->[$j]{main::key($num++,0,$l3,$gpu_data->[0]{'volts-gpu'}[1])} = $gpu_data->[0]{'volts-gpu'}[0]; + } } } eval $end if $b_log; @@ -23255,7 +23470,7 @@ sub sensors_output { sub ipmi_data { eval $start if $b_log; my ($program) = @_; - my ($b_cpu_0,$cmd,$file,@data,$fan_working,@row,$sys_fan_nu,$temp_working, + my ($b_cpu_0,$cmd,$file,@data,$fan_working,@row,$speed,$sys_fan_nu,$temp_working, $working_unit); my ($b_ipmitool,$i_key,$i_value,$i_unit); my $sensors = {}; @@ -23368,7 +23583,8 @@ sub ipmi_data { # note: can be cpu fan:, cpu fan speed:, etc. elsif ($row[$i_key] =~ /^(CPU|Processor)[\s_]Fan/i || $row[$i_key] =~ /^SYS\.[0-9][\s_]?\(CPU\s?0\)$/i){ - $sensors->{'fan-main'}->[1] = int($row[$i_value]); + $speed = int($row[$i_value]); + $sensors->{'fan-main'}->[1] = $speed if $speed < $max_fan; } # note that the counters are dynamically set for fan numbers here # otherwise you could overwrite eg aux fan2 with case fan2 in theory @@ -23378,6 +23594,7 @@ sub ipmi_data { elsif ($row[$i_key] =~ /^(SYS[\s_])?FAN[\s_]?([0-9A-F]+)/i){ $sys_fan_nu = hex($2); $fan_working = int($row[$i_value]); + next if $fan_working > $max_fan; $sensors->{'fan-default'} = () if !$sensors->{'fan-default'}; if ($sys_fan_nu =~ /^([0-9]+)$/){ # add to array if array index does not exist OR if number is > existing number @@ -23392,13 +23609,16 @@ sub ipmi_data { } } elsif ($row[$i_key] =~ /^(FAN PSU|PSU FAN)$/i){ - $sensors->{'fan-psu'} = int($row[$i_value]); + $speed = int($row[$i_value]); + $sensors->{'fan-psu'} = $speed if $speed < $max_fan; } elsif ($row[$i_key] =~ /^(FAN PSU1|PSU1 FAN)$/i){ - $sensors->{'fan-psu-1'} = int($row[$i_value]); + $speed = int($row[$i_value]); + $sensors->{'fan-psu-1'} = $speed if $speed < $max_fan; } elsif ($row[$i_key] =~ /^(FAN PSU2|PSU2 FAN)$/i){ - $sensors->{'fan-psu-2'} = int($row[$i_value]); + $speed = int($row[$i_value]); + $sensors->{'fan-psu-2'} = $speed if $speed < $max_fan; } if ($extra > 0){ if ($row[$i_key] =~ /^((.+\s|P[_]?)?\+?12V|PSU[12]_VOUT)$/i){ @@ -23435,12 +23655,11 @@ sub ipmi_data { print Data::Dumper::Dumper $sensors if $dbg[31]; return $sensors; } -sub lm_sensors_data { +sub linux_sensors_data { eval $start if $b_log; my $sensors = {}; my ($sys_fan_nu) = (0); my ($adapter,$fan_working,$temp_working,$working_unit) = ('','','','',''); - load_lm_sensors(); foreach $adapter (keys %{$sensors_raw->{'main'}}){ next if !$adapter || ref $sensors_raw->{'main'}{$adapter} ne 'ARRAY'; # not sure why hwmon is excluded, forgot to add info in comments @@ -23551,22 +23770,23 @@ sub lm_sensors_data { $sensors->{'temp-unit'} = set_temp_unit($sensors->{'temp-unit'},$working_unit) if $working_unit; } # note: can be cpu fan:, cpu fan speed:, etc. - elsif (!$sensors->{'fan-main'}->[1] && $_ =~ /^F?(CPU|Processor).*:([0-9]+)[\s]RPM/i){ - $sensors->{'fan-main'}->[1] = $2; + elsif (!defined $sensors->{'fan-main'}->[1] && $_ =~ /^F?(CPU|Processor).*:([0-9]+)[\s]RPM/i){ + $sensors->{'fan-main'}->[1] = $2 if $2 < $max_fan; } - elsif (!$sensors->{'fan-main'}->[2] && $_ =~ /^F?(M\/B|MB|SYS|Motherboard).*:([0-9]+)[\s]RPM/i){ - $sensors->{'fan-main'}->[2] = $2; + elsif (!defined $sensors->{'fan-main'}->[2] && $_ =~ /^F?(M\/B|MB|SYS|Motherboard).*:([0-9]+)[\s]RPM/i){ + $sensors->{'fan-main'}->[2] = $2 if $2 < $max_fan; } - elsif (!$sensors->{'fan-main'}->[3] && $_ =~ /F?(Power|P\/S|POWER).*:([0-9]+)[\s]RPM/i){ - $sensors->{'fan-main'}->[3] = $2; + elsif (!defined $sensors->{'fan-main'}->[3] && $_ =~ /F?(Power|P\/S|POWER).*:([0-9]+)[\s]RPM/i){ + $sensors->{'fan-main'}->[3] = $2 if $2 < $max_fan; } - elsif (!$sensors->{'fan-main'}->[4] && $_ =~ /F?(dimm|mem|sodimm).*:([0-9]+)[\s]RPM/i){ - $sensors->{'fan-main'}->[4] = $2; + elsif (!defined $sensors->{'fan-main'}->[4] && $_ =~ /F?(dimm|mem|sodimm).*:([0-9]+)[\s]RPM/i){ + $sensors->{'fan-main'}->[4] = $2 if $2 < $max_fan; } # note that the counters are dynamically set for fan numbers here # otherwise you could overwrite eg aux fan2 with case fan2 in theory # note: cpu/mobo/ps/sodimm are 1/2/3/4 elsif ($_ =~ /^F?(AUX|CASE|CHASSIS|FRONT|REAR).*:([0-9]+)[\s]RPM/i){ + next if $2 > $max_fan; $temp_working = $2; for (my $i = 5; $i < 30; $i++){ next if defined $sensors->{'fan-main'}->[$i]; @@ -23578,9 +23798,10 @@ sub lm_sensors_data { } # in rare cases syntax is like: fan1: xxx RPM elsif ($_ =~ /^FAN(1)?:([0-9]+)[\s]RPM/i){ - $sensors->{'fan-default'}->[1] = $2; + $sensors->{'fan-default'}->[1] = $2 if $2 < $max_fan; } elsif ($_ =~ /^FAN([2-9]|1[0-9]).*:([0-9]+)[\s]RPM/i){ + next if $2 > $max_fan; $fan_working = $2; $sys_fan_nu = $1; if ($sys_fan_nu =~ /^([0-9]+)$/){ @@ -23636,6 +23857,7 @@ sub lm_sensors_data { } } } + print Data::Dumper::Dumper $sensors if $dbg[31]; process_data($sensors) if %$sensors; main::log_data('dump','lm-sensors: %sensors',$sensors) if $b_log; @@ -23694,8 +23916,8 @@ sub load_lm_sensors { elsif ($adapter =~ /^(.*hwmon)-/){ $type = 'hwmon'; } - # ath/iwl: wifi; enp/eno/eth: lan nic - elsif ($adapter =~ /^(ath|iwl|en[op][0-9]|eth)[\S]+-/){ + # ath/iwl: wifi; enp/eno/eth/i350bb: lan nic + elsif ($adapter =~ /^(ath|i350bb|iwl|en[op][0-9]|eth)[\S]+-/){ $type = 'network'; } # put last just in case some other sensor type above had intel in name @@ -23703,7 +23925,7 @@ sub load_lm_sensors { $type = 'gpu'; } elsif ($adapter =~ /^(acpitz)-/ && $adapter !~ /^(acpitz-virtual)-/ ){ - $type = 'board'; + $type = 'acpitz'; } else { $type = 'main'; @@ -23726,6 +23948,159 @@ sub load_lm_sensors { main::log_data('dump','lm-sensors data: %$sensors_raw',$sensors_raw) if $b_log; eval $end if $b_log; } +sub load_sys_data { + eval $start if $b_log; + my ($device,$mon,$name,$label,$unit,$value,@values,%hwmons); + my ($j,$holder,$sensor,$type) = (0,'','',''); + my $glob = '/sys/class/hwmon/hwmon*/'; + $glob .= '{name,device,{curr,fan,in,power,temp}*_{input,label}}'; + my @hwmon = main::globber($glob); + # print Data::Dumper::Dumper \@sensors_data; + @hwmon = sort @hwmon; + push(@hwmon,'END'); + foreach my $item (@hwmon){ + next if ! -e $item; + $item =~ m|/sys/class/hwmon/(hwmon\d+)/|; + $mon = $1; + $mon =~ s/hwmon(\d)$/hwmon0$1/ if $mon =~ /hwmon\d$/; + # if it's a new hwmon, dump all previous data to avoid carry-over + if (!defined $hwmons{$mon}){ + $sensor = ''; + $holder = ''; + $j = 0; + } + if ($item =~ m/([^\/]+)_input$/){ + $sensor = $1; + $value = main::reader($item,'strip',0);; + } + # add the label to the just created _input item, if valid + elsif ($item =~ m/([^\/]+)_label$/){ + print "3: mon: $mon id: $sensor holder: $holder file: $item\n" if $dbg[51]; + # if this doesn't match, something unexpected happened, like no _input for + # _label item. Seen that, real. + next if !$holder || $1 ne $holder; + if (defined $hwmons{$mon}->{'sensors'}[$j]{'id'}){ + $sensor = $1; + $hwmons{$mon}->{'sensors'}[$j]{'label'} = main::reader($item,'strip',0); + } + } + if ($sensor && ($sensor ne $holder || $item eq 'END')){ + print "2: mon: $mon id: $sensor holder: $holder file: $item\n" if $dbg[51]; + # add the item, we'll add label after if it's located since it will be next + # in loop due to sort order. + if ($value){ + push(@{$hwmons{$mon}->{'sensors'}},{ + 'id' => $sensor, + 'value' => $value, + }); + $j = $#{$hwmons{$mon}->{'sensors'}}; + } + $holder = $sensor; + ($sensor,$value) = ('',undef,undef); + } + print "1: mon: $mon id: $sensor holder: $holder file: $item\n" if $dbg[51]; + # print "$item\n"; + if ($item =~ /name$/){ + $name = main::reader($item,'strip',0); + if ($name =~ /^(drive|nvme)/){ + $type = 'disk'; + } + elsif ($name =~ /^(BAT)/i){ + $type = 'bat'; + } + # intel on die io controller, like southbridge/northbridge used to be + elsif ($name =~ /^(pch)/){ + $type = 'pch'; + } + elsif ($name =~ /^(.*hwmon)/){ + $type = 'hwmon'; + } + # ath/iwl: wifi; enp/eno/eth/i350bb: lan nic + elsif ($name =~ /^(ath|i350|iwl|en[op][0-9]|eth)[\S]/){ + $type = 'network'; + } + # put last just in case some other sensor type above had intel in name + elsif ($name =~ /^(amdgpu|intel|nouveau|radeon)/){ + $type = 'gpu'; + } + # not confirmed in /sys that name will be acpitz-virtual, verify + elsif ($name =~ /^(acpitz)/ && $name !~ /^(acpitz-virtual)/ ){ + $type = 'acpitz'; + } + else { + $type = 'main'; + } + $hwmons{$mon}->{'name'} = $name; + $hwmons{$mon}->{'type'} = $type; + } + elsif ($item =~ /device$/){ + $device = readlink($item); + print "device: $device\n" if $dbg[51]; + $device =~ s|^.*/||; + $hwmons{$mon}->{'device'} = $device; + } + } + print '/sys/class/hwmon raw: ', Data::Dumper::Dumper \%hwmons if $dbg[18]; + main::log_data('dump','/sys data raw: %hwmons',\%hwmons) if $b_log; + # $sensors_raw->{$type}{$adapter} = [@values]; + foreach my $hwmon (sort keys %hwmons){ + my $adapter = $hwmons{$hwmon}->{'name'}; + $hwmons{$hwmon}->{'device'} =~ s/^0000://; + $adapter .= '-' . $hwmons{$hwmon}->{'device'}; + @values = (); + foreach my $item (@{$hwmons{$hwmon}->{'sensors'}}){ + my $name = ($item->{'label'}) ? $item->{'label'}: $item->{'id'}; + if ($item->{'id'} =~ /^temp/){ + $unit = 'C'; + $value = sprintf('%0.1f',$item->{'value'}/1000); + } + elsif ($item->{'id'} =~ /^fan/){ + $unit = 'RPM'; + $value = $item->{'value'}; + } + # note: many sensors require further math on value, so these will be wrong + # in many cases since this is not running the math on the results like + # lm-sensors will do if sensors are detected and loaded and configured. + elsif ($item->{'id'} =~ /^in\d/){ + if ($item->{'value'} >= 1000){ + $unit = 'V'; + $value = sprintf('%0.2f',$item->{'value'}/1000) + 0; + if ($hwmons{$hwmon}->{'type'} eq 'main' && $name =~ /^in\d/){ + if ($value >= 10 && $value <= 14){ + $name = '12V'; + } + elsif ($value >= 4 && $value <= 6){ + $name = '5V'; + } + # vbat can be 3, 3.3, but so can 3.3V board + } + } + else { + $unit = 'mV'; + $value = $item->{'value'}; + } + } + elsif ($item->{'id'} =~ /^power/){ + $unit = 'W'; + $value = sprintf('%0.1f',$item->{'value'}/1000); + } + my $string = $name . ':' . $value . " $unit"; + push(@values,$string); + } +# if ($hwmons{$hwmon}->{'type'} eq 'acpitz' && $hwmons{$hwmon}->{'device'}){ +# my $tz ='/sys/class/thermal/' . $hwmons{$hwmon}->{'device'} . '/type'; +# if (-e $tz){ +# my $tz_type = main::reader($tz,'strip',0),"\n"; +# } +# } + if (@values){ + $sensors_raw->{$hwmons{$hwmon}->{'type'}}{$adapter} = [@values]; + } + } + print '/sys/class/hwmon processed: ' , Data::Dumper::Dumper $sensors_raw if $dbg[18]; + main::log_data('dump','/sys data: %$sensors_raw',$sensors_raw) if $b_log; + eval $end if $b_log; +} # bsds sysctl may have hw.sensors data sub sysctl_data { @@ -23734,6 +24109,7 @@ sub sysctl_data { my $sensors = {}; # assume always starts at 0, can't do dynamic because freebsd shows tz1 first my $add = 1; + print Data::Dumper::Dumper $sysctl{'sensor'} if $dbg[18];; foreach (@{$sysctl{'sensor'}}){ my ($sensor,$type,$number,$value); if (/^hw\.sensors\.([a-z]+)([0-9]+)\.(cpu|temp|fan|volt)([0-9])/){ @@ -23776,7 +24152,7 @@ sub sysctl_data { $sensors->{'temp' . $number} = $value; } elsif ($type eq 'fan' && !defined $sensors->{'fan-main'}->[$number]){ - $sensors->{'fan-main'}->[$number] = $value; + $sensors->{'fan-main'}->[$number] = $value if $value < $max_fan; } elsif ($type eq 'volt'){ if ($working =~ /\+3\.3V/i){ @@ -24184,8 +24560,8 @@ sub gpu_sensor_data { elsif (/^[^:]+:([0-9\.]+)\s+W\s/i){ $gpu_data->[$j]{'watts'} = $1; } - elsif (/^[^:]+:([0-9\.]+)\s+mV\s/i){ - $gpu_data->[$j]{'mvolts'} = $1; + elsif (/^[^:]+:([0-9\.]+)\s+(m?V)\s/i){ + $gpu_data->[$j]{'volts-gpu'} = [$1,$2]; } } } @@ -26684,6 +27060,7 @@ sub set_xprop { } +## DeviceData # creates arrays: $devices{'audio'}; $devices{'graphics'}; $devices{'hwraid'}; # $devices{'network'}; $devices{'timer'} and local @devices for logging/debugging # 0 type @@ -27442,6 +27819,24 @@ sub pci_class { } } +# if > 1, returns first found, not going to be too granular with this yet. +sub get_device_temp { + eval $start if $b_log; + my $bus_id = $_[0]; + my $glob = "/sys/devices/pci*/*/*:$bus_id/hwmon/hwmon*/temp*_input"; + my @files = main::globber($glob); + my $temp; + foreach my $file (@files){ + $temp = main::reader($file,'strip',0); + if ($temp){ + $temp = sprintf('%0.1f',$temp/1000); + last; + } + } + eval $end if $b_log; + return $temp; +} + ## DiskDataBSD # handles disks and partition extra data for disks bsd, raid-zfs, # partitions, swap, unmounted diff --git a/inxi.1 b/inxi.1 index e9f1974..ad9368d 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 "2022\-08\-06" "inxi" "inxi manual" +.TH INXI 1 "2022\-10\-08" "inxi" "inxi manual" .SH NAME inxi \- Command line system information script for console and IRC @@ -318,8 +318,9 @@ Deprecated. See \fB\-G \-a\fR. .B \-G \fR, \fB\-\-graphics\fR Show Graphic device(s) information, including details of device and display drivers (\fBX:\fR \fBloaded:\fR, and, if applicable: \fBunloaded:\fR, -\fBfailed:\fR, and active \fBgpu:\fR drivers), display protocol (if available), -display server (and/or Wayland compositor), vendor and version number, e.g.: +\fBfailed:\fR, \fBdri:\fR (if X and different from loaded X drivers) drivers, +and active \fBgpu:\fR drivers), display protocol (if available), display server +(and/or Wayland compositor), vendor and version number, e.g.: \fBDisplay: x11 server: Xorg v: 1.15.1\fR @@ -689,12 +690,18 @@ then shows what package(s) you need to install to add support for each feature. .TP .B \-s \fR, \fB\-\-sensors\fR -Show output from sensors if sensors installed/configured: Motherboard/CPU/GPU -temperatures; detected fan speeds. GPU temperature when available. Nvidia shows -screen number for multiple screens. IPMI sensors are also used (root required) -if present. See Advanced options \fB\-\-sensors\-use\fR or -\fB\-\-sensors\-exclude\fR if you want to use only a subset of all sensors, or -exclude one. +Show output from sensors if sensors installed/configured: Motherboard/CPU/GPU +temperatures; detected fan speeds. GPU temperature when available. Nvidia shows +screen number for multiple screens. IPMI sensors are also used (root required) +if present. + +See Advanced options \fB\-\-sensors\-use\fR or \fB\-\-sensors\-exclude\fR if you +want to use only a subset of all sensors, or exclude one (currently only for +\fBlm\-sensors\fR and \fB/sys\fR sourced data). + +For current Linux, will fallback gracefully to using \fB/sys/class/hwmon\fR as +sensor data source if \fBlm\-sensors\fR is not installed. You can compare the +two by using \fB\-\-force sensors\-sys\fR option with \fB\-s\fR. . .TP .B \-\-slots\fR @@ -998,7 +1005,6 @@ Konversation etc. Setting a specific color type removes the global color selection. - .TP .B \-\-indent [11\-xx]\fR Change primary wide indent width. Generally useless. Only applied if output @@ -1208,7 +1214,7 @@ dds rev version to optical drive. .TP .B \-x \-D\fR -\- Adds HDD temperature with disk data. +\- Adds drive temperature with disk data. Method 1: Systems running Linux kernels ~5.6 and newer should have \fBdrivetemp\fR module data available. If so, drive temps will come from @@ -1269,6 +1275,8 @@ specific vendor [product] information. \- \fBX.org:\fR Adds (for single GPU, nvidia driver) screen number that GPU is running on. +\- Adds device temperature for each discrete device (Linux only). + .TP .B \-x \-i\fR \- Adds IP v6 additional scope data, like Global, Site, Temporary for @@ -1343,6 +1351,8 @@ specific vendor [product] information. \- Adds PCI/USB ID of each device. +\- Adds device temperature for each discrete device (Linux only). + .TP .B \-x \-o\fR, \fB\-x \-p\fR, \fB\-x \-P\fR \- Adds \fBmapper:\fR (the \fB/dev/mapper/\fR partition ID) @@ -1363,8 +1373,8 @@ bitmap (if present). Resync line, shows blocks synced/total blocks. .TP .B \-x \-s\fR -\- Adds basic voltages: 12v, 5v, 3.3v, vbat (\fBipmi\fR, \fBlm-sensors\fR if -present). +\- Adds basic voltages: 12v, 5v, 3.3v, vbat (\fBipmi\fR, \fBlm\-sensors / +/sys/class/hwmon\fR if present). .TP .B \-x \-S\fR @@ -1402,12 +1412,6 @@ found for each distribution system base detection. .B \-xx \-B\fR \- Adds serial number. -.TP -.B \-xx \-C\fR -\- Adds microarchitecture level (v1,v2,v3,v4) (Intel and AMD 64 bit CPUs only). -This information is used for setting compile time optimization switches in for -example GCC. These levels were introduced in 2020. - .TP .B \-xx \-D\fR \- Adds disk serial number. @@ -1825,6 +1829,13 @@ shown). only will show if Core generation, otherwise the arch value is enough. For AMD, only shows Zen generation. +\- Adds microarchitecture \fBlevel:\fR (v1,v2,v3,v4) (64 bit Intel/AMD CPUs +only). This information is used for setting compile time optimization switches +in for example GCC. These levels were introduced in 2020. + +Because this a CPU flag based test, and these levels when > 2 are not always +100% based on exposed CPU flags (eg OSXSAVE), for > v2, adds \fBnote: check\fR. + \- Adds CPU family, model\-id, and stepping (replaces \fBrev\fR of \fB\-Cx\fR). Format is \fBhexadecimal (decimal)\fR if greater than 9, otherwise \fBhexadecimal\fR. @@ -1919,6 +1930,8 @@ differences shown, like \fBcores:\fR, \fBmin/max:\fR, etc. type: MT MCP MCM SMP arch: Zen gen: 1 + level: v3 + note: check process: GF 14nm built: 2017\-19 family:0x17 (23) @@ -2073,8 +2086,8 @@ Graphics: active: DVI\-I\-1,VGA\-1 empty: HDMI\-A\-1 bus\-ID: 0a:00.0 chip\-ID: 1002:68f9 class\-ID: 0300 Display: x11 server: X.Org v: 1.21.1.3 with: Xwayland v: 22.1.0 - compositor: xfwm v: 4.16.1 driver: X: loaded: modesetting gpu: radeon - display\-ID: :0.0 screens: 1 + compositor: xfwm v: 4.16.1 driver: X: loaded: modesetting dri: r600 + gpu: radeon display\-ID: :0.0 screens: 1 Screen\-1: 0 s-res: 2560x1024 s-dpi: 96 s\-size: 677x270mm (26.65x10.63") s\-diag: 729mm (28.7") Monitor\-1: DVI\-I\-1 pos: primary,left model: Samsung SyncMaster @@ -2294,7 +2307,7 @@ Shortcut. See \fB\-\-force dmidecode\fR. Force inxi to use Curl, Fetch, Perl, or Wget for downloads. .TP -.B \-\-force [colors|dmidecode|hddtemp|lsusb|rpm|usb-sys|wayland|vmstat|wmctrl]\fR +.B \-\-force [option(s)]\fR Various force options to allow users to override defaults. Values can be given as a comma separated list: @@ -2330,7 +2343,14 @@ For systems that support running rpm along with the primary package installer if any, installed, so the command runs in those cases (if inxi can determine it is running in that type of system). -\- \fBusb-sys\fR \- Forces the USB data generator to use \fB/sys\fR as data +\- \fBsensors\-sys\fR \- Force use of \fB/sys/class/hwmon\fR data for sensors +(excluding ipmi sensors, which are their own line if present), skip +\fBlm\-sensors\fR. Generally useful for testing since sys data is used if no +lm\-sensors data was found anyway, but if \fBlm\-sensors\fR was installed, and +returned no data, it's most likely if not nearly certain that \fB/sys\fR will +also not return data. + +\- \fBusb\-sys\fR \- Forces the USB data generator to use \fB/sys\fR as data source instead of \fBlsusb\fR (Linux only). \- \fBvmstat\fR \- Forces use of vmstat for memory data. @@ -2423,19 +2443,24 @@ on a one time basis. .TP .B \-\-sensors\-exclude\fR -Similar to \fB\-\-sensors\-use\fR except removes listed sensors from sensor -data. Make permanent with \fBSENSORS_EXCLUDE\fR configuration item. Note that -gpu, network, disk, and other specific device monitor chips are excluded by +Linux only. Similar to \fB\-\-sensors\-use\fR except removes listed sensors from +sensor data. Make permanent with \fBSENSORS_EXCLUDE\fR configuration item. Note +that gpu, network, disk, and other specific device monitor chips are excluded by default. Example: \fBinxi \-sxx \-\-sensors\-exclude k10temp-pci-00c3\fR +.TP +.B \-\-sensors\-sys\fR +Shortcut. See \fB\-\-force sensors\-sys\fR + .TP .B \-\-sensors\-use\fR -Use only the (comma separated) sensor arrays for \fB\-s\fR output. Make -permanent with \fBSENSORS_USE\fR configuration item. Sensor array ID value -must be the exact value shown in lm\-sensors sensors output (Linux/lm-sensors -only). If you only want to exclude one (or more) sensors from the output, +Linux only. Use only the (comma separated) sensor arrays for \fB\-s\fR output. +Make permanent with \fBSENSORS_USE\fR configuration item. Sensor array ID value +must be the exact value shown in lm\-sensors sensors output (lm-sensors only) or +use \fB\-s \-\-dbg 18\fR ('main' =>.. section) to see the sensor ID strings used +internally. If you only want to exclude one (or more) sensors from the output, use \fB\-\-sensors\-exclude\fR. Can be useful if the default sensor data used by inxi is not from the right diff --git a/inxi.changelog b/inxi.changelog index 9c94a8c..48e1707 100644 --- a/inxi.changelog +++ b/inxi.changelog @@ -1,3 +1,171 @@ +================================================================================ +Version: 3.3.22 +Patch: 00 +Date: 2022-10-08 +-------------------------------------------------------------------------------- +RELEASE NOTES: +-------------------------------------------------------------------------------- + +Another big one, with a long time to-do item done! /sys based sensors data is +now used as a fallback, with fully revised error messages to handle this new +sensor data variant. Due to potential bugs this might create, this was left off +of the 3.3.21 release, which needed to go out on a schedule, but there is plenty +of time for 3.3.22 to be debugged. + +-------------------------------------------------------------------------------- +KNOWN ISSUES: + +1. inxi can't currently handle raw in[0-9] voltage sensor data from +/sys/class/hwmon, that may get corrected, but I've honestly never seen a system +that shows raw in[0-9] values as field names, so it's probably not very +pressing, but it can happen. Similar that is to how default fanx and tempx field +names are processed. + +2. Currently only checking -Gx, -Nx device temp for bus IDs ending in .0, which +is the primary PCI device. I think that's the only one that will have a temp, +.1, which is a second device on the same hardware, doesn't have that data in +tests. Saves some requests since it's a big glob of /sys. + +3. Spiral Linux has no obvious way to determine that it is Spiral and not Debian +11 as base distro. No /etc/ files for distro ID contain anything for spiral, so +leaving that one alone. + +4. Can't get 100% reliable cpu level > v2 due to it not being a pure cpu flag +based test, which is kind of sadly typical for the originators of this idea, but +since the choice was dump the feature, or just use the note: check for > v2, +opted for note: check. One wants to ask questions here, but honestly I already +know the answer so why bother asking the question... The docs for this are +awful, inadequate, incomplete. + +My strong suspicion is that this is NOT intended to be a distro-wide feature +beyond v2 support minimum, but rather is for specific compile options for a +package or daemon or server or whatever that can benefit from this type of +fine-tuning. One thinks of Gentoo for example back when such fine-tunings could +actually deliver noticeable differences in performance. A per system type +feature that is, not a distro-wide feature. At least that's my initial feeling, +but this is probably about all the time I will spend on it since inxi can't get +it more accurate anyway. + +-------------------------------------------------------------------------------- +BUGS: + +1. Bug in monitor position logic, the horizontal/vertical sorts were being done +alphanumerically, leading to absurd results where 800 > 2560 or whatever. +Basically all x / y positions less than 1000 would have forced the smaller +number to be considered as the greatest value. Another corner case find by +mrmazda. Thanks mrmazda! + +-------------------------------------------------------------------------------- +FIXES: + +1. Added i350bb sensor to network sensor type. + +2. Small glitch with some scenarios with missing fan1 in sensors, showed fan1 0 +rpm, but then showed fan 3: empty. That was a slight error in how undefined vs +'' empty was treated. + +3. Added fix for defective fan speeds, skip fan item if > 15000, which is a bug +in the fan speed report, making it useless. Seen 65535 reported RPM. Could +probably make it 10000 upper limit but suspect that is a simple bug that creates +an absurd value, 2^16 so won't be anything high unless bug active. This fix runs +for ipmi, linux, and sysctl fan data. + +4. Trying for fix for dynamic gpu voltage, assumed always mV, but might be V. + +5. Inadequate or obscure or non-existent redhat/suse documentation led to some +fixes for cpu v levels. Note that level v3/v4 can't be fully determined by cpu +flag tests, but who cares? Certainly not me. Added 'note: check' for v3/v4. + +6. Nvidia device arch id was too loose, false id for non existing lovelace arch. +Note that due to array reverse, the newest ids will always run first, which +leads to possible false positives with first string match tests when no product +IDs are available yet. + +-------------------------------------------------------------------------------- +ENHANCEMENTS: + +1. Elbrus CPU arch, process, year, arch data made more complete using new data +resource. Thanks Elbrus guys! + +2. Finally, raw, basic /sys/class/hwmon temp data. Linux kernel docs note +supports temp, fan, volts, amps, energy. But have only seen temps so far. Can +force /sys use with --force sensors-sys / --sensors-sys, though there's no point +to doing that except to test. + +Also changed --recommends to note lm-sensors not required for sensor data now. + +3. Adding device temp for -Gx, -Nx. Will only work for Linux and when found, and +only for free drivers (I think). + +4. Added xdriinfo based dri drivers (with fallback to Xorg.0.log as data source, +not as accurate), that will show if and only if that driver is not the same name +as a detected X or gpu driver. + +5. Another big upgrade to cp_cpu_arch, added and corrected many AMD/Intel +matches. + +6. A few more gpu product ids, Intel, added. + +7. More disk vendors, ids, the list, as we are now well aware, is endless, +reflecting perhaps the futility of pursuing the infinite using finite means. + +-------------------------------------------------------------------------------- +CHANGES: + +1. Slight changes in how inxi supplies no sensor data messages, and in the +fallback cases and handling. More accurate and precise, and more robust overall. + +2. Due to complexity of understanding level: and the fact not all cpu flags are +exposed that are required, moved -Cxx level: to -Ca. + +3. Changing slightly inaccurate Sound Server for ALSA/OSS to Sound API, which is +the closest I can come to explaining clearly what it is. Note that you can only +load one API type audio subsystem/driver, so you will be running one or the +other, never both, from what I understand. + +Since OpenBSD sndio includes sndiod, calling that a sound server is basically +fine, since it's both the server and the interface, if I understand it right, +and there won't be a second sound server listed, actually won't be for any BSD +that I know of, it's going to be sndio or OSS or nothing, unless something has +changed. + +-------------------------------------------------------------------------------- +DOCUMENTATION: + +1. Man page, updates for /sys/class/hwmon based sensor data. + +2. Small update for cpu level v3/v4, added note: check explanation, though it's +too hard to really explain this stuff since the docs are... not wonderful, when +they even exist and don't contradict each other. + +-------------------------------------------------------------------------------- +CODE: + +1. Refined significantly sensors missing data and error messages to be much more +accurate and granular. Also enables more sensors tools, though hopefully they +won't appear since those are a real pain to implement, but it's more open to +being sensor tool agnostic now due to these refinements than before. + +2. Added xdpiinfo to debugger. + +3. Switched x_drivers to return ref of array of refs, use join for output only, +that lets us use the drivers to test dri stuff also (if we want or need to), and +keeps it consistent with how most of inxi does that type of data +handling/testing. If undef, it means no array ref exists, which makes testing +easy. + +Not truly understanding hash/array refs when inxi rewrite to Perl started is +probably one of the bigger causes of glitches and ongoing optimizations. +Basically, in all but very small array cases, it's almost always better to start +with a ref from the start as soon as the hash/array moves between functions, +with one exception, when it's a globally stored data item. Then it depends. But +this requires a consistent testing for null data as well, which is harder if you +did it in different ways from the start. But slowly and surely chipping away at +these. + +-------------------------------------------------------------------------------- +-- Harald Hope - Sat, 7 Oct 2022 11:15:12 -0700 + ================================================================================ Version: 3.3.21 Patch: 00