Skip to content

Commit a602543

Browse files
gxglassGideon Glass
andauthored
Refactor getDiskStatistics() and add stats for bytes read and bytes written (#12247)
* Refactor getDiskStats() now that it returns about 10 values. Compute byte stats from sector level stats by assuming sectors are 512 bytes and using multiplication. Rationale for why sectors can be assumed to be 512 bytes is given in a comment. Add byte level rate stats to the trace output emitted in customSystemMonitor(). Testing: passed 99995 runs here: 20250715-001140-gglass-90a10b1f1bdd221a compressed=True data_size=41293265 duration=5559400 ended=100000 fail=5 fail_fast=10 max_runs=100000 pass=99995 priority=100 remaining=0 runtime=1:26:20 sanity=False started=100000 stopped=20250715-013800 submitted=20250715-001140 timeout=5400 username=gglass I couldn't figure out about the 5 failures but I don't think they are related to my change. * Fix compile error on MacOS relating to readMilliSecs et al now being members of diskStats --------- Co-authored-by: Gideon Glass <[email protected]>
1 parent f3c43df commit a602543

File tree

3 files changed

+154
-167
lines changed

3 files changed

+154
-167
lines changed

flow/Platform.actor.cpp

Lines changed: 98 additions & 124 deletions
Original file line numberDiff line numberDiff line change
@@ -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

945954
dev_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)
13671351
std::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;

flow/SystemMonitor.cpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -132,8 +132,11 @@ SystemStatistics customSystemMonitor(std::string const& eventName, StatisticsSta
132132
.detail("DiskWriteSeconds", currentStats.processDiskWriteSeconds)
133133
.detail("DiskReadsCount", currentStats.processDiskReadCount)
134134
.detail("DiskWritesCount", currentStats.processDiskWriteCount)
135-
.detail("DiskWriteSectors", currentStats.processDiskWriteSectors)
136135
.detail("DiskReadSectors", currentStats.processDiskReadSectors)
136+
.detail("DiskWriteSectors", currentStats.processDiskWriteSectors)
137+
.detail("DiskReadBytes", currentStats.processDiskReadBytes)
138+
.detail("DiskWriteBytes", currentStats.processDiskWriteBytes)
139+
137140
.detail("FileWrites", netData.countFileLogicalWrites - statState->networkState.countFileLogicalWrites)
138141
.detail("FileReads", netData.countFileLogicalReads - statState->networkState.countFileLogicalReads)
139142
.detail("CacheReadBytes",

0 commit comments

Comments
 (0)