"Fossies" - the Fresh Open Source Software Archive

Member "xosview-1.23/linux/diskmeter.cc" (11 Jul 2020, 9253 Bytes) of package /linux/misc/xosview-1.23.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "diskmeter.cc" see the Fossies "Dox" file reference documentation.

    1 //
    2 //  Copyright (c) 1999, 2006 by Mike Romberg (mike.romberg@noaa.gov)
    3 //
    4 //  This file may be distributed under terms of the GPL
    5 //
    6 
    7 #include "diskmeter.h"
    8 #include <stdlib.h>
    9 #include <stdio.h>
   10 #include <sys/types.h>
   11 #include <sys/stat.h>
   12 #include <string.h>
   13 #include <dirent.h>
   14 #include <errno.h>
   15 #include <limits.h>
   16 #include <fstream>
   17 #include <iostream>
   18 #include <string>
   19 
   20 #define MAX_PROCSTAT_LENGTH 4096
   21 
   22 
   23 DiskMeter::DiskMeter( XOSView *parent, float max ) : FieldMeterGraph(
   24   parent, 3, "DISK", "READ/WRITE/IDLE"), _vmstat(false),
   25   _statFileName("/proc/stat")
   26 {
   27     read_prev_ = 0;
   28     write_prev_ = 0;
   29     maxspeed_ = max;
   30 
   31     _sysfs=_vmstat=false;
   32     struct stat buf;
   33 
   34     // first - try sysfs:
   35     if (stat("/sys/block", &buf) == 0
   36       && buf.st_mode & S_IFDIR) {
   37 
   38         _sysfs = true;
   39         _statFileName = "/sys/block";
   40         XOSDEBUG("diskmeter: using sysfs /sys/block\n");
   41         getsysfsdiskinfo();
   42 
   43     } else  // try vmstat:
   44     if (stat("/proc/vmstat", &buf) == 0
   45       && buf.st_mode & S_IFREG) {
   46 
   47         _vmstat = true;
   48         _sysfs  = false;
   49         _statFileName = "/proc/vmstat";
   50         getvmdiskinfo();
   51 
   52     } else // fall back to stat
   53         getdiskinfo();
   54 
   55 
   56 }
   57 
   58 DiskMeter::~DiskMeter( void )
   59     {
   60     }
   61 
   62 void DiskMeter::checkResources( void )
   63     {
   64     FieldMeterGraph::checkResources();
   65 
   66     setfieldcolor( 0, parent_->getResource("diskReadColor") );
   67     setfieldcolor( 1, parent_->getResource("diskWriteColor") );
   68     setfieldcolor( 2, parent_->getResource("diskIdleColor") );
   69     priority_ = atoi (parent_->getResource( "diskPriority" ) );
   70     dodecay_ = parent_->isResourceTrue("diskDecay" );
   71     useGraph_ = parent_->isResourceTrue( "diskGraph" );
   72     SetUsedFormat(parent_->getResource("diskUsedFormat"));
   73     }
   74 
   75 void DiskMeter::checkevent( void )
   76     {
   77     if (_vmstat)
   78         getvmdiskinfo();
   79     else if ( _sysfs )
   80         getsysfsdiskinfo();
   81     else
   82         getdiskinfo();
   83 
   84     drawfields();
   85 
   86     }
   87 
   88 // IMHO the logic here is quite broken - but for backward compat UNCHANGED:
   89 void DiskMeter::updateinfo(unsigned long one, unsigned long two,
   90   int fudgeFactor)
   91     {
   92     // assume each "unit" is 1k.
   93     // This is true for ext2, but seems to be 512 bytes
   94     // for vfat and 2k for cdroms
   95     // work in 512-byte blocks
   96 
   97     // tw: strange, on my system, a ext2fs (read and write)
   98     // unit seems to be 2048. kernel 2.2.12 and the file system
   99     // is on a SW-RAID5 device (/dev/md0).
  100 
  101     // So this is a FIXME - but how ???
  102 
  103     float itim = IntervalTimeInMicrosecs();
  104     unsigned long read_curr = one * fudgeFactor;  // FIXME!
  105     unsigned long write_curr = two * fudgeFactor; // FIXME!
  106 
  107     // avoid strange values at first call
  108     if(read_prev_ == 0) read_prev_ = read_curr;
  109     if(write_prev_ == 0) write_prev_ = write_curr;
  110 
  111     // calculate rate in bytes per second
  112     fields_[0] = ((read_curr - read_prev_) * 1e6 * 512) / itim;
  113     fields_[1] = ((write_curr - write_prev_) * 1e6 * 512) / itim;
  114 
  115     // fix overflow (conversion bug?)
  116     if (fields_[0] < 0.0)
  117         fields_[0] = 0.0;
  118     if (fields_[1] < 0.0)
  119         fields_[1] = 0.0;
  120 
  121     if (fields_[0] + fields_[1] > total_)
  122         total_ = fields_[0] + fields_[1];
  123 
  124     fields_[2] = total_ - (fields_[0] + fields_[1]);
  125 
  126     read_prev_ = read_curr;
  127     write_prev_ = write_curr;
  128 
  129     setUsed((fields_[0]+fields_[1]), total_);
  130     IntervalTimerStart();
  131     }
  132 
  133 void DiskMeter::getvmdiskinfo(void)
  134 {
  135     IntervalTimerStop();
  136     total_ = maxspeed_;
  137     char buf[MAX_PROCSTAT_LENGTH];
  138     std::ifstream stats(_statFileName);
  139     unsigned long one, two;
  140 
  141     if ( !stats )
  142         {
  143         std::cerr <<"Can not open file : " << _statFileName << std::endl;
  144         exit( 1 );
  145         }
  146 
  147 
  148     stats >> buf;
  149     // kernel >= 2.5
  150     while (!stats.eof() && strncmp(buf, "pgpgin", 7))
  151         {
  152         stats.ignore(1024, '\n');
  153         stats >> buf;
  154         }
  155 
  156     // read first value
  157     stats >> one;
  158 
  159     while (!stats.eof() && strncmp(buf, "pgpgout", 7))
  160         {
  161         stats.ignore(1024, '\n');
  162         stats >> buf;
  163         }
  164 
  165     // read second value
  166     stats >> two;
  167 
  168     updateinfo(one, two, 4);
  169 }
  170 
  171 void DiskMeter::getdiskinfo( void )
  172 {
  173     IntervalTimerStop();
  174     total_ = maxspeed_;
  175     char buf[MAX_PROCSTAT_LENGTH];
  176     std::ifstream stats(_statFileName);
  177 
  178     if ( !stats )
  179     {
  180         std::cerr <<"Can not open file : " << _statFileName << std::endl;
  181         exit( 1 );
  182     }
  183 
  184     // Find the line with 'page'
  185     stats >> buf;
  186     while (strncmp(buf, "disk_io:", 8))
  187     {
  188         stats.ignore(MAX_PROCSTAT_LENGTH, '\n');
  189         stats >> buf;
  190         if (stats.eof())
  191             break;
  192     }
  193 
  194     // read values
  195     unsigned long one=0, two=0;
  196     unsigned long junk,read1,write1;
  197     stats >> buf;
  198     while (7 == sscanf(buf,"(%lu,%lu):(%lu,%lu,%lu,%lu,%lu)",&junk,&junk,&junk,&junk,&read1,&junk,&write1))
  199     {
  200         one += read1;
  201         two += write1;
  202         stats >> buf;
  203     }
  204 
  205     updateinfo(one, two, 1);
  206 }
  207 
  208 // sysfs version - works with long-long !!
  209 void DiskMeter::update_info(const diskmap &reads, const diskmap &writes)
  210 {
  211     float itim = IntervalTimeInMicrosecs();
  212     // the sum of all disks
  213     unsigned long long all_bytes_read = 0, all_bytes_written = 0;
  214     unsigned int sect_size = 512; // from linux-3.10/Documentation/block/stat.txt
  215 
  216     // avoid strange values at first call
  217     // (by this - the first value displayed becomes zero)
  218     if (sysfs_read_prev_.empty())
  219     {
  220         sysfs_read_prev_  = reads;
  221         sysfs_write_prev_ = writes;
  222         itim = 1;   // itim is garbage here too. Valgrind complains.
  223     }
  224 
  225     for (diskmap::const_iterator it = reads.begin(); it != reads.end(); it++)
  226     {
  227         if (it->second < sysfs_read_prev_[it->first]) // counter wrapped
  228             all_bytes_read += ULONG_MAX - sysfs_read_prev_[it->first] + it->second;
  229         else
  230             all_bytes_read += it->second - sysfs_read_prev_[it->first];
  231     }
  232     for (diskmap::const_iterator it = writes.begin(); it != writes.end(); it++)
  233     {
  234         if (it->second < sysfs_write_prev_[it->first]) // counter wrapped
  235             all_bytes_written += ULONG_MAX - sysfs_write_prev_[it->first] + it->second;
  236         else
  237             all_bytes_written += it->second - sysfs_write_prev_[it->first];
  238     }
  239 
  240     all_bytes_read *= sect_size;
  241     all_bytes_written *= sect_size;
  242     XOSDEBUG("disk: read: %llu, written: %llu\n", all_bytes_read, all_bytes_written);
  243 
  244     // convert rate from bytes/microsec into bytes/second
  245     fields_[0] = all_bytes_read * ( 1e6 / itim );
  246     fields_[1] = all_bytes_written * ( 1e6 / itim );
  247 
  248     // fix overflow (conversion bug?)
  249     if (fields_[0] < 0.0)
  250         fields_[0] = 0.0;
  251     if (fields_[1] < 0.0)
  252         fields_[1] = 0.0;
  253 
  254     // bump up max total:
  255     if (fields_[0] + fields_[1] > total_)
  256         total_ = fields_[0] + fields_[1];
  257 
  258     fields_[2] = total_ - (fields_[0] + fields_[1]);
  259 
  260     // save old vals for next round
  261     sysfs_read_prev_  = reads;
  262     sysfs_write_prev_ = writes;
  263 
  264     setUsed(fields_[0] + fields_[1], total_);
  265     IntervalTimerStart();
  266 }
  267 
  268 // XXX: sysfs - read Documentation/iostats.txt !!!
  269 // extract stats from /sys/block/*/stat
  270 // each disk reports an unsigned long, which can WRAP around
  271 void DiskMeter::getsysfsdiskinfo( void )
  272 {
  273         // field-3: sects read since boot (but can wrap!)
  274         // field-7: sects written since boot (but can wrap!)
  275         // just sum up everything in /sys/block/*/stat
  276 
  277   std::string sysfs_dir = _statFileName;
  278   std::string disk, tmp;
  279   std::ifstream diskstat;
  280   struct stat buf;
  281   char line[128];
  282   unsigned long vals[7];
  283   diskmap reads, writes;
  284 
  285   IntervalTimerStop();
  286   total_ = maxspeed_;
  287   sysfs_dir += '/';
  288 
  289   DIR *dir = opendir(_statFileName);
  290   if (dir == NULL) {
  291     XOSDEBUG("sysfs: Cannot open directory : %s\n", _statFileName);
  292     return;
  293   }
  294 
  295   // visit every /sys/block/*/stat and sum up the values:
  296   for (struct dirent *dirent; (dirent = readdir(dir)) != NULL; ) {
  297     if (strncmp(dirent->d_name, ".", 1) == 0 ||
  298         strncmp(dirent->d_name, "..", 2) == 0 ||
  299         strncmp(dirent->d_name, "loop", 4) == 0 ||
  300         strncmp(dirent->d_name, "ram", 3) == 0)
  301       continue;
  302 
  303     disk = sysfs_dir + dirent->d_name;
  304     if (stat(disk.c_str(), &buf) == 0 && buf.st_mode & S_IFDIR) {
  305       // only scan for real HW (raid, md, and lvm all mapped on them)
  306       tmp = disk + "/device";
  307       if (lstat(tmp.c_str(), &buf) != 0 || (buf.st_mode & S_IFLNK) == 0)
  308         continue;
  309 
  310       // is a dir, locate 'stat' file in it
  311       disk += "/stat";
  312       diskstat.open(disk.c_str());
  313       if (diskstat.good()) {
  314         diskstat.getline(line, 128);
  315         char *cur = line, *end = NULL;
  316         for (int i = 0; i < 7; i++) {
  317           vals[i] = strtoul(cur, &end, 10);
  318           cur = end;
  319         }
  320         reads[dirent->d_name]  = vals[2];
  321         writes[dirent->d_name] = vals[6];
  322 
  323         XOSDEBUG("disk stat: %s | read: %lu, written: %lu\n", disk.c_str(), vals[2], vals[6]);
  324         diskstat.close();
  325         diskstat.clear();
  326       }
  327       else
  328         XOSDEBUG("disk stat open: %s - errno=%d\n", disk.c_str(), errno);
  329     }
  330     else
  331       XOSDEBUG("disk is not dir: %s - errno=%d\n", disk.c_str(), errno);
  332   } // for
  333   closedir(dir);
  334   update_info(reads, writes);
  335 }