]> git.proxmox.com Git - pve-common.git/blobdiff - src/PVE/ProcFSTools.pm
proc fs tools: handle proc/stat without guest values
[pve-common.git] / src / PVE / ProcFSTools.pm
index e413ccf1c50b1f90575f9cb9a764c45fcc73a962..d636427688d2554254505012e71e7b8160b204aa 100644 (file)
@@ -5,6 +5,7 @@ use warnings;
 use POSIX;
 use Time::HiRes qw (gettimeofday);
 use IO::File;
+use List::Util qw(sum);
 use PVE::Tools;
 use Cwd qw();
 
@@ -35,6 +36,7 @@ sub read_cpuinfo {
     my $fh = IO::File->new ($fn, "r");
     return $res if !$fh;
 
+    my $cpuid = 0;
     my $idhash = {};
     my $count = 0;
     while (defined(my $line = <$fh>)) {
@@ -47,7 +49,10 @@ sub read_cpuinfo {
        } elsif ($line =~ m/^flags\s*:\s*(.*)$/) {
            $res->{flags} = $1 if !length $res->{flags};
        } elsif ($line =~ m/^physical id\s*:\s*(\d+)\s*$/i) {
-           $idhash->{$1} = 1;
+           $cpuid = $1;
+           $idhash->{$1} = 1 if not defined($idhash->{$1});
+       } elsif ($line =~ m/^cpu cores\s*:\s*(\d+)\s*$/i) {
+           $idhash->{$cpuid} = $1 if defined($idhash->{$cpuid});
        }
     }
 
@@ -56,6 +61,8 @@ sub read_cpuinfo {
 
     $res->{sockets} = scalar(keys %$idhash) || 1;
 
+    $res->{cores} = sum(values %$idhash) || 1;
+
     $res->{cpus} = $count;
 
     $fh->close;
@@ -125,22 +132,54 @@ sub read_loadavg {
     return wantarray ? (0, 0, 0) : 0;
 }
 
+sub parse_pressure {
+    my ($path) = @_;
+
+    my $res = {};
+    my $v = qr/\d+\.\d+/;
+    my $fh = IO::File->new($path, "r") or return undef;
+    while (defined (my $line = <$fh>)) {
+       if ($line =~ /^(some|full)\s+avg10\=($v)\s+avg60\=($v)\s+avg300\=($v)\s+total\=(\d+)/) {
+           $res->{$1}->{avg10} = $2;
+           $res->{$1}->{avg60} = $3;
+           $res->{$1}->{avg300} = $4;
+           $res->{$1}->{total} = $4;
+       }
+    }
+    $fh->close;
+    return $res;
+}
+
+sub read_pressure {
+    my $res = {};
+    foreach my $type (qw(cpu memory io)) {
+       my $stats = parse_pressure("/proc/pressure/$type");
+       $res->{$type} = $stats if $stats;
+    }
+    return $res;
+}
+
 my $last_proc_stat;
 
 sub read_proc_stat {
-    my $res = { user => 0, nice => 0, system => 0, idle => 0 , sum => 0};
+    my $res = { user => 0, nice => 0, system => 0, idle => 0 , iowait => 0, irq => 0, softirq => 0, steal => 0, guest => 0, guest_nice => 0, sum => 0};
 
     my $cpucount = 0;
 
     if (my $fh = IO::File->new ("/proc/stat", "r")) {
        while (defined (my $line = <$fh>)) {
-           if ($line =~ m|^cpu\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s|) {
-               $res->{user} = $1;
-               $res->{nice} = $2;
+           if ($line =~ m|^cpu\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)(?:\s+(\d+)\s+(\d+))?|) {
+               $res->{user} = $1 - ($9 // 0);
+               $res->{nice} = $2 - ($10 // 0);
                $res->{system} = $3;
                $res->{idle} = $4;
-               $res->{used} = $1+$2+$3;
+               $res->{used} = $1+$2+$3+$6+$7+$8;
                $res->{iowait} = $5;
+               $res->{irq} = $6;
+               $res->{softirq} = $7;
+               $res->{steal} = $8;
+               $res->{guest} = $9 // 0;
+               $res->{guest_nice} = $10 // 0;
            } elsif ($line =~ m|^cpu\d+\s|) {
                $cpucount++;
            }
@@ -152,6 +191,18 @@ sub read_proc_stat {
 
     my $ctime = gettimeofday; # floating point time in seconds
 
+    # the sum of all fields
+    $res->{total} = $res->{user}
+       + $res->{nice}
+       + $res->{system}
+       + $res->{iowait}
+       + $res->{irq}
+       + $res->{softirq}
+       + $res->{steal}
+       + $res->{idle}
+       + $res->{guest}
+       + $res->{guest_nice};
+
     $res->{ctime} = $ctime;
     $res->{cpu} = 0;
     $res->{wait} = 0;
@@ -163,11 +214,15 @@ sub read_proc_stat {
     if ($diff > 1000) { # don't update too often
        my $useddiff =  $res->{used} - $last_proc_stat->{used};
        $useddiff = $diff if $useddiff > $diff;
-       $res->{cpu} = $useddiff/$diff;
+
+       my $totaldiff = $res->{total} - $last_proc_stat->{total};
+       $totaldiff = $diff if $totaldiff > $diff;
+
+       $res->{cpu} = $useddiff/$totaldiff;
 
        my $waitdiff =  $res->{iowait} - $last_proc_stat->{iowait};
        $waitdiff = $diff if $waitdiff > $diff;
-       $res->{wait} = $waitdiff/$diff;
+       $res->{wait} = $waitdiff/$totaldiff;
 
        $last_proc_stat = $res;
     } else {
@@ -344,6 +399,7 @@ sub decode_mount {
 
 sub parse_mounts {
     my ($mounts) = @_;
+
     my $mntent = [];
     while ($mounts =~ /^\s*([^#].*)$/gm) {
        # lines from the file are encoded so we can just split at spaces
@@ -352,11 +408,14 @@ sub parse_mounts {
        # in glibc's parser frequency and pass seem to be optional
        $freq = $1 if $opts =~ s/\s+(\d+)$//;
        $passno = $1 if $opts =~ s/\s+(\d+)$//;
-       push @$mntent, [decode_mount($what),
-                       decode_mount($dir),
-                       decode_mount($fstype),
-                       decode_mount($opts),
-                       $freq, $passno];
+       push @$mntent, [
+           decode_mount($what),
+           decode_mount($dir),
+           decode_mount($fstype),
+           decode_mount($opts),
+           $freq,
+           $passno,
+       ];
     }
     return $mntent;
 }