ohmu
·
2026-10-15
1#include "common/printing.h"
2#include "common/jsonconfig.h"
3#include "common/size.h"
4#include "common/strutil.h"
5#include "detection/diskio/diskio.h"
6#include "modules/diskio/diskio.h"
7
8static int sortDevices(const FFDiskIOResult* left, const FFDiskIOResult* right) {
9 return ffStrbufComp(&left->name, &right->name);
10}
11
12static void formatKey(const FFDiskIOOptions* options, FFDiskIOResult* dev, uint32_t index, FFstrbuf* key) {
13 if (options->moduleArgs.key.length == 0) {
14 ffStrbufSetF(key, "%s (%s)", FF_MODULE_GET_DISPLAY_NAME(DiskIO), dev->name.length ? dev->name.chars : dev->devPath.chars);
15 } else {
16 ffStrbufClear(key);
17 FF_PARSE_FORMAT_STRING_CHECKED(key, &options->moduleArgs.key, ((FFformatarg[]) {
18 FF_ARG(index, "index"),
19 FF_ARG(dev->name, "name"),
20 FF_ARG(dev->devPath, "dev-path"),
21 FF_ARG(options->moduleArgs.keyIcon, "icon"),
22 FF_ARG(FF_MODULE_GET_DISPLAY_NAME(DiskIO), "module-name"),
23 }));
24 }
25}
26
27bool ffPrintDiskIO(FFDiskIOOptions* options) {
28 FF_LIST_AUTO_DESTROY result = ffListCreate();
29 const char* error = ffDetectDiskIO(&result, options);
30
31 if (error) {
32 ffPrintError(FF_MODULE_GET_DISPLAY_NAME(DiskIO), 0, &options->moduleArgs, FF_PRINT_TYPE_NO_CUSTOM_KEY, "%s", error);
33 return false;
34 }
35
36 ffListSort(&result, sizeof(FFDiskIOResult), (const void*) sortDevices);
37
38 uint32_t index = 0;
39 FF_STRBUF_AUTO_DESTROY key = ffStrbufCreate();
40 FF_STRBUF_AUTO_DESTROY buffer = ffStrbufCreate();
41 FF_STRBUF_AUTO_DESTROY buffer2 = ffStrbufCreate();
42
43 FF_LIST_FOR_EACH (FFDiskIOResult, dev, result) {
44 formatKey(options, dev, result.length == 1 ? 0 : index + 1, &key);
45 ffStrbufClear(&buffer);
46
47 if (options->moduleArgs.outputFormat.length == 0) {
48 ffPrintLogoAndKey(key.chars, 0, &options->moduleArgs, FF_PRINT_TYPE_NO_CUSTOM_KEY);
49
50 ffSizeAppendNum(dev->bytesRead, &buffer);
51 if (!options->detectTotal) {
52 ffStrbufAppendS(&buffer, "/s");
53 }
54 ffStrbufAppendS(&buffer, " (R) - ");
55
56 ffSizeAppendNum(dev->bytesWritten, &buffer);
57 if (!options->detectTotal) {
58 ffStrbufAppendS(&buffer, "/s");
59 }
60 ffStrbufAppendS(&buffer, " (W)");
61 ffStrbufPutTo(&buffer, stdout);
62 } else {
63 ffSizeAppendNum(dev->bytesRead, &buffer);
64 if (!options->detectTotal) {
65 ffStrbufAppendS(&buffer, "/s");
66 }
67 ffStrbufClear(&buffer2);
68 ffSizeAppendNum(dev->bytesWritten, &buffer2);
69 if (!options->detectTotal) {
70 ffStrbufAppendS(&buffer2, "/s");
71 }
72
73 FF_PRINT_FORMAT_CHECKED(key.chars, 0, &options->moduleArgs, FF_PRINT_TYPE_NO_CUSTOM_KEY, ((FFformatarg[]) {
74 FF_ARG(buffer, "size-read"),
75 FF_ARG(buffer2, "size-written"),
76 FF_ARG(dev->name, "name"),
77 FF_ARG(dev->devPath, "dev-path"),
78 FF_ARG(dev->bytesRead, "bytes-read"),
79 FF_ARG(dev->bytesWritten, "bytes-written"),
80 FF_ARG(dev->readCount, "read-count"),
81 FF_ARG(dev->writeCount, "write-count"),
82 }));
83 }
84 ++index;
85 }
86
87 FF_LIST_FOR_EACH (FFDiskIOResult, dev, result) {
88 ffStrbufDestroy(&dev->name);
89 ffStrbufDestroy(&dev->devPath);
90 }
91
92 return true;
93}
94
95void ffParseDiskIOJsonObject(FFDiskIOOptions* options, yyjson_val* module) {
96 yyjson_val *key, *val;
97 size_t idx, max;
98 yyjson_obj_foreach (module, idx, max, key, val) {
99 if (ffJsonConfigParseModuleArgs(key, val, &options->moduleArgs)) {
100 continue;
101 }
102
103 if (unsafe_yyjson_equals_str(key, "namePrefix")) {
104 ffStrbufSetJsonVal(&options->namePrefix, val);
105 continue;
106 }
107
108 if (unsafe_yyjson_equals_str(key, "detectTotal")) {
109 options->detectTotal = yyjson_get_bool(val);
110 continue;
111 }
112
113 if (unsafe_yyjson_equals_str(key, "waitTime")) {
114 if (!ffJsonConfigParseUInt32(val, &options->waitTime, UINT32_MAX)) {
115 ffPrintError(FF_MODULE_GET_DISPLAY_NAME(DiskIO), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "Property 'waitTime' must be a non-negative integer no greater than 4294967295");
116 continue;
117 }
118 if (options->waitTime == 0) {
119 options->waitTime = 1;
120 }
121 continue;
122 }
123
124 ffPrintError(FF_MODULE_GET_DISPLAY_NAME(DiskIO), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "Unknown JSON key %s", unsafe_yyjson_get_str(key));
125 }
126}
127
128[[gnu::cold]]
129void ffGenerateDiskIOJsonConfig(FFDiskIOOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module) {
130 ffJsonConfigGenerateModuleArgsConfig(doc, module, &options->moduleArgs);
131
132 yyjson_mut_obj_add_strbuf(doc, module, "namePrefix", &options->namePrefix);
133
134 yyjson_mut_obj_add_bool(doc, module, "detectTotal", options->detectTotal);
135
136 yyjson_mut_obj_add_uint(doc, module, "waitTime", options->waitTime);
137}
138
139bool ffGenerateDiskIOJsonResult(FFDiskIOOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module) {
140 FF_LIST_AUTO_DESTROY result = ffListCreate();
141 const char* error = ffDetectDiskIO(&result, options);
142
143 if (error) {
144 yyjson_mut_obj_add_str(doc, module, "error", error);
145 return false;
146 }
147
148 yyjson_mut_val* arr = yyjson_mut_obj_add_arr(doc, module, "result");
149 FF_LIST_FOR_EACH (FFDiskIOResult, dev, result) {
150 yyjson_mut_val* obj = yyjson_mut_arr_add_obj(doc, arr);
151 yyjson_mut_obj_add_strbuf(doc, obj, "name", &dev->name);
152 yyjson_mut_obj_add_strbuf(doc, obj, "devPath", &dev->devPath);
153 yyjson_mut_obj_add_uint(doc, obj, "bytesRead", dev->bytesRead);
154 yyjson_mut_obj_add_uint(doc, obj, "bytesWritten", dev->bytesWritten);
155 yyjson_mut_obj_add_uint(doc, obj, "readCount", dev->readCount);
156 yyjson_mut_obj_add_uint(doc, obj, "writeCount", dev->writeCount);
157 }
158
159 FF_LIST_FOR_EACH (FFDiskIOResult, dev, result) {
160 ffStrbufDestroy(&dev->name);
161 ffStrbufDestroy(&dev->devPath);
162 }
163
164 return true;
165}
166
167void ffInitDiskIOOptions(FFDiskIOOptions* options) {
168 ffOptionInitModuleArg(&options->moduleArgs, "");
169
170 ffStrbufInit(&options->namePrefix);
171 options->detectTotal = false;
172 options->waitTime = 250;
173}
174
175void ffDestroyDiskIOOptions(FFDiskIOOptions* options) {
176 ffOptionDestroyModuleArg(&options->moduleArgs);
177 ffStrbufDestroy(&options->namePrefix);
178}
179
180FFModuleBaseInfo ffDiskIOModuleInfo = {
181 .name = "DiskIO",
182 .description = "Print physical disk I/O throughput",
183 .displayName = {
184 .en = "Disk I/O",
185 .ar = "إدخال/إخراج القرص",
186 .cs = "Diskové I/O",
187 .de = "Festplatten I/O",
188 .es = "E/S del disco",
189 .fi = "Levyn I/O",
190 .fr = "Entrée/Sortie disque",
191 .gl = "E/S do disco",
192 .he = "קלט/פלט דיסק",
193 .id = "I/O Disk",
194 .it = "I/O del disco",
195 .ja = "ディスク I/O",
196 .ko = "디스크 I/O",
197 .pl = "Dysk I/O",
198 .pt = "Disco I/O",
199 .ru = "Дисковый I/O",
200 .tr = "Disk G/Ç",
201 .uk = "Диск В/В",
202 .vi = "Đĩa I/O",
203 .zh_CN = "磁盘 I/O",
204 .zh_TW = "磁碟 I/O",
205 },
206 .initOptions = (void*) ffInitDiskIOOptions,
207 .destroyOptions = (void*) ffDestroyDiskIOOptions,
208 .parseJsonObject = (void*) ffParseDiskIOJsonObject,
209 .printModule = (void*) ffPrintDiskIO,
210 .generateJsonResult = (void*) ffGenerateDiskIOJsonResult,
211 .generateJsonConfig = (void*) ffGenerateDiskIOJsonConfig,
212 .formatArgs = FF_FORMAT_ARG_LIST(((FFModuleFormatArg[]) {
213 { "Size of data read [per second] (formatted)" , "size-read" },
214 { "Size of data written [per second] (formatted)", "size-written" },
215 { "Device name *", "name" },
216 { "Device raw file path *", "dev-path" },
217 { "Size of data read [per second] (in bytes)", "bytes-read" },
218 { "Size of data written [per second] (in bytes)", "bytes-written" },
219 { "Number of reads", "read-count" },
220 { "Number of writes", "write-count" },
221 })),
222 .defaultOrder = 68,
223};