@@ -828,24 +828,18 @@ void getMachineLoad(uint64_t& idleTime, uint64_t& totalTime, bool logDetails) {
828828 .detail (" Guest" , t_guest);
829829}
830830
831- void getDiskStatistics (std::string const & directory,
832- uint64_t & currentIOs,
833- uint64_t & readMilliSecs,
834- uint64_t & writeMilliSecs,
835- uint64_t & IOMilliSecs,
836- uint64_t & reads,
837- uint64_t & writes,
838- uint64_t & writeSectors,
839- uint64_t & readSectors) {
831+ // This is inside the __linux__ ifdef
832+ DiskStatistics getDiskStatistics (std::string const & directory) {
840833 INJECT_FAULT (platform_error, " getDiskStatistics" ); // Getting disks statistics failed
841- currentIOs = 0 ;
842834
843835 struct stat buf;
844836 if (stat (directory.c_str (), &buf)) {
845837 TraceEvent (SevError, " GetDiskStatisticsStatError" ).detail (" Directory" , directory).GetLastError ();
846838 throw platform_error ();
847839 }
848840
841+ DiskStatistics diskStats;
842+
849843 std::ifstream proc_stream (" /proc/diskstats" , std::ifstream::in);
850844 while (proc_stream.good ()) {
851845 std::string line;
@@ -917,29 +911,44 @@ void getDiskStatistics(std::string const& directory,
917911 disk_stream >> ticks;
918912 disk_stream >> aveq;
919913
920- currentIOs = cur_ios;
921- readMilliSecs = rd_ticks;
922- writeMilliSecs = wr_ticks;
923- IOMilliSecs = ticks;
924- reads = rd_ios;
925- writes = wr_ios;
926- writeSectors = wr_sectors;
927- readSectors = rd_sectors;
928-
929- // TraceEvent("DiskMetricsRaw").detail("Input", line).detail("Ignore", ignore).detail("RdIos", rd_ios)
930- // .detail("RdMerges", rd_merges).detail("RdSectors", rd_sectors).detail("RdTicks",
931- // rd_ticks).detail("WrIos", wr_ios).detail("WrMerges", wr_merges) .detail("WrSectors",
932- // wr_sectors).detail("WrTicks", wr_ticks).detail("CurIos", cur_ios).detail("Ticks", ticks).detail("Aveq",
933- // aveq) .detail("CurrentIOs", currentIOs).detail("BusyTicks", busyTicks).detail("Reads",
934- // reads).detail("Writes", writes).detail("WriteSectors", writeSectors)
935- // .detail("ReadSectors", readSectors);
936- return ;
937- } else
914+ diskStats.currentIOs = cur_ios;
915+ diskStats.readMilliSecs = rd_ticks;
916+ diskStats.writeMilliSecs = wr_ticks;
917+ diskStats.IOMilliSecs = ticks;
918+ diskStats.reads = rd_ios;
919+ diskStats.writes = wr_ios;
920+ diskStats.readSectors = rd_sectors;
921+ diskStats.writeSectors = wr_sectors;
922+
923+ // Argument for why we believe that Linux sectors are exactly 512 bytes:
924+ //
925+ // 1) In general in UNIX user<->kernel interfaces I believe this has been true for 30+ years
926+ //
927+ // 2) Concretely, in Linux, we have the following documentation (reviewed July 2025):
928+ // a) RE: /proc/diskstats: https://www.kernel.org/doc/html/latest/admin-guide/iostats.html,
929+ // says that /proc/diskstats (which we are reading in code just above) generates
930+ // output identical to /sys/block/<device>/stat.
931+ // b) RE: /sys/block/<device>/stat: https://www.kernel.org/doc/html/latest/block/stat.html
932+ // says "The sectors in question are the standard UNIX 512-byte sectors".
933+ //
934+ // One might wonder, why doesn't Linux just count bytes in 64-bit counters?
935+ // Well, who knows. 32 bits of quantized 512-byte sectors ought to be enough for anybody, right?
936+ // It's left as an exercise for the reader to calculate how frequently these counters
937+ // will overflow on a moderately busy system where a device is doing 1MB/sec.
938+ diskStats.readBytes = rd_sectors * uint64_t { 512 };
939+ diskStats.writeBytes = wr_sectors * uint64_t { 512 };
940+
941+ return diskStats;
942+ } else {
938943 disk_stream.ignore (std::numeric_limits<std::streamsize>::max (), ' \n ' );
944+ }
939945 }
940946
941- if (!g_network->isSimulated ())
947+ if (!g_network->isSimulated ()) {
942948 TraceEvent (SevWarn, " GetDiskStatisticsDeviceNotFound" ).detail (" Directory" , directory);
949+ }
950+
951+ return diskStats;
943952}
944953
945954dev_t getDeviceId (std::string path) {
@@ -1061,24 +1070,10 @@ void getMachineLoad(uint64_t& idleTime, uint64_t& totalTime, bool logDetails) {
10611070 // need to add logging here to TraceEvent
10621071}
10631072
1064- void getDiskStatistics (std::string const & directory,
1065- uint64_t & currentIOs,
1066- uint64_t & readMilliSecs,
1067- uint64_t & writeMilliSecs,
1068- uint64_t & IOMilliSecs,
1069- uint64_t & reads,
1070- uint64_t & writes,
1071- uint64_t & writeSectors,
1072- uint64_t & readSectors) {
1073+ // XXX: this is inside __FreeBSD__ ifdef.
1074+ // This is not likely to be well tested.
1075+ DiskStatistics getDiskStatistics (std::string const & directory) {
10731076 INJECT_FAULT (platform_error, " getDiskStatistics" ); // getting disk stats failed
1074- currentIOs = 0 ;
1075- readMilliSecs = 0 ; // This will not be used because we cannot get its value.
1076- writeMilliSecs = 0 ; // This will not be used because we cannot get its value.
1077- IOMilliSecs = 0 ;
1078- reads = 0 ;
1079- writes = 0 ;
1080- writeSectors = 0 ;
1081- readSectors = 0 ;
10821077
10831078 struct stat buf;
10841079 if (stat (directory.c_str (), &buf)) {
@@ -1098,9 +1093,10 @@ void getDiskStatistics(std::string const& directory,
10981093
10991094 int dn;
11001095 u_int64_t total_transfers_read, total_transfers_write;
1101- u_int64_t total_blocks_read, total_blocks_write ;
1096+ u_int64_t total_bytes_read, total_bytes_written ;
11021097 u_int64_t queue_len;
11031098 long double ms_per_transaction;
1099+ DiskStatistics diskStats;
11041100
11051101 dscur.dinfo = (struct devinfo *)calloc (1 , sizeof (struct devinfo ));
11061102 if (dscur.dinfo == nullptr ) {
@@ -1116,7 +1112,6 @@ void getDiskStatistics(std::string const& directory,
11161112 num_devices = dscur.dinfo ->numdevs ;
11171113
11181114 for (dn = 0 ; dn < num_devices; dn++) {
1119-
11201115 if (devstat_compute_statistics (&dscur.dinfo ->devices [dn],
11211116 nullptr ,
11221117 etime,
@@ -1126,23 +1121,25 @@ void getDiskStatistics(std::string const& directory,
11261121 &total_transfers_read,
11271122 DSM_TOTAL_TRANSFERS_WRITE,
11281123 &total_transfers_write,
1129- DSM_TOTAL_BLOCKS_READ ,
1130- &total_blocks_read ,
1131- DSM_TOTAL_BLOCKS_WRITE ,
1132- &total_blocks_write ,
1124+ DSM_TOTAL_BYTES_READ ,
1125+ &total_bytes_read ,
1126+ DSM_TOTAL_BYTES_WRITE ,
1127+ &total_bytes_written ,
11331128 DSM_QUEUE_LENGTH,
11341129 &queue_len,
11351130 DSM_NONE) != 0 ) {
11361131 TraceEvent (SevError, " GetDiskStatisticsStatError" ).GetLastError ();
11371132 throw platform_error ();
11381133 }
11391134
1140- currentIOs += queue_len;
1141- IOMilliSecs += (u_int64_t )ms_per_transaction;
1142- reads += total_transfers_read;
1143- writes += total_transfers_write;
1144- writeSectors += total_blocks_read;
1145- readSectors += total_blocks_write;
1135+ diskStats.currentIOs += queue_len;
1136+ diskStats.IOMilliSecs += (u_int64_t )ms_per_transaction;
1137+ diskStats.reads += total_transfers_read;
1138+ diskStats.writes += total_transfers_write;
1139+ diskStats.readSectors += total_bytes_read / 512 ;
1140+ diskStats.writeSectors += total_bytes_written / 512 ;
1141+ diskStats.readBytes += total_bytes_read;
1142+ diskStats.writeBytes += total_bytes_written;
11461143 }
11471144}
11481145
@@ -1245,22 +1242,9 @@ void getMachineLoad(uint64_t& idleTime, uint64_t& totalTime, bool logDetails) {
12451242 r_load.cpu_ticks [CPU_STATE_SYSTEM];
12461243}
12471244
1248- void getDiskStatistics (std::string const & directory,
1249- uint64_t & currentIOs,
1250- uint64_t & readMilliSecs,
1251- uint64_t & writeMilliSecs,
1252- uint64_t & IOMilliSecs,
1253- uint64_t & reads,
1254- uint64_t & writes,
1255- uint64_t & writeSectors,
1256- uint64_t & readSectors) {
1245+ // This is inside the __APPLE__ ifdef
1246+ DiskStatistics getDiskStatistics (std::string const & directory) {
12571247 INJECT_FAULT (platform_error, " getDiskStatistics" ); // Getting disk stats failed (macOS)
1258- currentIOs = 0 ; // This will not be used because we cannot get its value.
1259- readMilliSecs = 0 ;
1260- writeMilliSecs = 0 ;
1261- IOMilliSecs = 0 ;
1262- writeSectors = 0 ;
1263- readSectors = 0 ;
12641248
12651249 struct statfs buf;
12661250 if (statfs (directory.c_str (), &buf)) {
@@ -1331,37 +1315,37 @@ void getDiskStatistics(std::string const& directory,
13311315
13321316 CFNumberRef number;
13331317
1318+ DiskStatistics diskStats;
1319+
13341320 if ((number = (CFNumberRef)CFDictionaryGetValue (stats_dict, CFSTR (kIOBlockStorageDriverStatisticsReadsKey )))) {
1335- CFNumberGetValue (number, kCFNumberSInt64Type , &reads);
1321+ CFNumberGetValue (number, kCFNumberSInt64Type , &diskStats. reads );
13361322 }
13371323
13381324 if ((number = (CFNumberRef)CFDictionaryGetValue (stats_dict, CFSTR (kIOBlockStorageDriverStatisticsWritesKey )))) {
1339- CFNumberGetValue (number, kCFNumberSInt64Type , &writes);
1325+ CFNumberGetValue (number, kCFNumberSInt64Type , &diskStats. writes );
13401326 }
13411327
13421328 uint64_t nanoSecs;
13431329 if ((number =
13441330 (CFNumberRef)CFDictionaryGetValue (stats_dict, CFSTR (kIOBlockStorageDriverStatisticsTotalReadTimeKey )))) {
13451331 CFNumberGetValue (number, kCFNumberSInt64Type , &nanoSecs);
1346- readMilliSecs += nanoSecs;
1347- IOMilliSecs += nanoSecs;
1332+ diskStats. readMilliSecs += nanoSecs / 1000000 ;
1333+ diskStats. IOMilliSecs += nanoSecs / 1000000 ;
13481334 }
13491335 if ((number =
13501336 (CFNumberRef)CFDictionaryGetValue (stats_dict, CFSTR (kIOBlockStorageDriverStatisticsTotalWriteTimeKey )))) {
13511337 CFNumberGetValue (number, kCFNumberSInt64Type , &nanoSecs);
1352- writeMilliSecs += nanoSecs;
1353- IOMilliSecs += nanoSecs;
1338+ diskStats. writeMilliSecs += nanoSecs / 1000000 ;
1339+ diskStats. IOMilliSecs += nanoSecs / 1000000 ;
13541340 }
1355- // nanoseconds to milliseconds
1356- readMilliSecs /= 1000000 ;
1357- writeMilliSecs /= 1000000 ;
1358- IOMilliSecs /= 1000000 ;
13591341
13601342 CFRelease (disk_dict);
13611343 IOObjectRelease (disk);
13621344 IOObjectRelease (disk_list);
1345+
1346+ return diskStats;
13631347}
1364- #endif
1348+ #endif // __APPLE__
13651349
13661350#if defined(_WIN32)
13671351std::vector<std::string> expandWildcardPath (const char * wildcardPath) {
@@ -1461,17 +1445,18 @@ struct SystemStatisticsState {
14611445 : Query(nullptr ), QueueLengthCounter(nullptr ), DiskTimeCounter(nullptr ), ReadsCounter(nullptr ),
14621446 WritesCounter(nullptr ), WriteBytesCounter(nullptr ), ProcessorIdleCounter(nullptr ), lastTime(0 ),
14631447 lastClockThread(0 ), lastClockProcess(0 ), processLastSent(0 ), processLastReceived(0 ) {}
1448+
14641449#elif defined(__unixish__)
14651450 uint64_t machineLastSent, machineLastReceived;
14661451 uint64_t machineLastOutSegs, machineLastRetransSegs;
1467- uint64_t lastReadMilliSecs, lastWriteMilliSecs, lastIOMilliSecs, lastReads, lastWrites, lastWriteSectors,
1468- lastReadSectors;
1452+
1453+ DiskStatistics lastDiskStats;
1454+
14691455 uint64_t lastClockIdleTime, lastClockTotalTime;
14701456 SystemStatisticsState ()
14711457 : lastTime(0 ), lastClockThread(0 ), lastClockProcess(0 ), processLastSent(0 ), processLastReceived(0 ),
14721458 machineLastSent(0 ), machineLastReceived(0 ), machineLastOutSegs(0 ), machineLastRetransSegs(0 ),
1473- lastReadMilliSecs(0 ), lastWriteMilliSecs(0 ), lastIOMilliSecs(0 ), lastReads(0 ), lastWrites(0 ),
1474- lastWriteSectors(0 ), lastReadSectors(0 ), lastClockIdleTime(0 ), lastClockTotalTime(0 ) {}
1459+ lastClockIdleTime(0 ), lastClockTotalTime(0 ) {}
14751460#else
14761461#error Port me!
14771462#endif
@@ -1747,51 +1732,40 @@ SystemStatistics getSystemStatistics(std::string const& dataFolder,
17471732 (*statState)->machineLastSent = machineNowSent;
17481733 (*statState)->machineLastReceived = machineNowReceived;
17491734 (*statState)->machineLastOutSegs = machineOutSegs;
1750- (*statState)->machineLastRetransSegs = machineRetransSegs;
17511735
1752- uint64_t currentIOs;
1753- uint64_t nowReadMilliSecs = (*statState)->lastReadMilliSecs ;
1754- uint64_t nowWriteMilliSecs = (*statState)->lastWriteMilliSecs ;
1755- uint64_t nowIOMilliSecs = (*statState)->lastIOMilliSecs ;
1756- uint64_t nowReads = (*statState)->lastReads ;
1757- uint64_t nowWrites = (*statState)->lastWrites ;
1758- uint64_t nowWriteSectors = (*statState)->lastWriteSectors ;
1759- uint64_t nowReadSectors = (*statState)->lastReadSectors ;
1736+ (*statState)->machineLastRetransSegs = machineRetransSegs;
17601737
17611738 if (dataFolder != " " ) {
1762- getDiskStatistics (dataFolder,
1763- currentIOs,
1764- nowReadMilliSecs,
1765- nowWriteMilliSecs,
1766- nowIOMilliSecs,
1767- nowReads,
1768- nowWrites,
1769- nowWriteSectors,
1770- nowReadSectors);
1771- returnStats.processDiskQueueDepth = currentIOs;
1772- returnStats.processDiskReadCount = nowReads;
1773- returnStats.processDiskWriteCount = nowWrites;
1739+ DiskStatistics currentDiskStats = getDiskStatistics (dataFolder);
1740+
1741+ returnStats.processDiskQueueDepth = currentDiskStats.currentIOs ;
1742+ returnStats.processDiskReadCount = currentDiskStats.reads ;
1743+ returnStats.processDiskWriteCount = currentDiskStats.writes ;
1744+
17741745 if (returnStats.initialized ) {
17751746 returnStats.processDiskIdleSeconds = std::max<double >(
17761747 0 ,
17771748 returnStats.elapsed -
1778- std::min<double >(returnStats.elapsed , (nowIOMilliSecs - (*statState)->lastIOMilliSecs ) / 1000.0 ));
1749+ std::min<double >(returnStats.elapsed ,
1750+ (currentDiskStats.IOMilliSecs - (*statState)->lastDiskStats .IOMilliSecs ) /
1751+ 1000.0 ));
17791752 returnStats.processDiskReadSeconds =
1780- std::min<double >(returnStats.elapsed , (nowReadMilliSecs - (*statState)->lastReadMilliSecs ) / 1000.0 );
1781- returnStats.processDiskWriteSeconds =
1782- std::min<double >(returnStats.elapsed , (nowWriteMilliSecs - (*statState)->lastWriteMilliSecs ) / 1000.0 );
1783- returnStats.processDiskRead = (nowReads - (*statState)->lastReads );
1784- returnStats.processDiskWrite = (nowWrites - (*statState)->lastWrites );
1785- returnStats.processDiskWriteSectors = (nowWriteSectors - (*statState)->lastWriteSectors );
1786- returnStats.processDiskReadSectors = (nowReadSectors - (*statState)->lastReadSectors );
1753+ std::min<double >(returnStats.elapsed ,
1754+ (currentDiskStats.readMilliSecs - (*statState)->lastDiskStats .readMilliSecs ) / 1000.0 );
1755+ returnStats.processDiskWriteSeconds = std::min<double >(
1756+ returnStats.elapsed ,
1757+ (currentDiskStats.writeMilliSecs - (*statState)->lastDiskStats .writeMilliSecs ) / 1000.0 );
1758+ returnStats.processDiskRead = (currentDiskStats.reads - (*statState)->lastDiskStats .reads );
1759+ returnStats.processDiskWrite = (currentDiskStats.writes - (*statState)->lastDiskStats .writes );
1760+ returnStats.processDiskReadSectors =
1761+ (currentDiskStats.readSectors - (*statState)->lastDiskStats .readSectors );
1762+ returnStats.processDiskWriteSectors =
1763+ (currentDiskStats.writeSectors - (*statState)->lastDiskStats .writeSectors );
1764+ returnStats.processDiskReadBytes = (currentDiskStats.readBytes - (*statState)->lastDiskStats .readBytes );
1765+ returnStats.processDiskWriteBytes = (currentDiskStats.writeBytes - (*statState)->lastDiskStats .writeBytes );
17871766 }
1788- (*statState)->lastIOMilliSecs = nowIOMilliSecs;
1789- (*statState)->lastReadMilliSecs = nowReadMilliSecs;
1790- (*statState)->lastWriteMilliSecs = nowWriteMilliSecs;
1791- (*statState)->lastReads = nowReads;
1792- (*statState)->lastWrites = nowWrites;
1793- (*statState)->lastWriteSectors = nowWriteSectors;
1794- (*statState)->lastReadSectors = nowReadSectors;
1767+
1768+ (*statState)->lastDiskStats = currentDiskStats;
17951769 }
17961770
17971771 uint64_t clockIdleTime = (*statState)->lastClockIdleTime ;
0 commit comments