Bug Summary

File:.build-ci/../nvme-print-stdout-top.c
Warning:line 1496, column 12
Potential leak of memory pointed to by 'subsys_name'

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple x86_64-pc-linux-gnu -analyze -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name nvme-print-stdout-top.c -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -mrelocation-model pic -pic-level 2 -pic-is-pie -mframe-pointer=none -fmath-errno -ffp-contract=on -fno-rounding-math -mconstructor-aliases -funwind-tables=2 -target-cpu x86-64 -tune-cpu generic -debugger-tuning=gdb -fdebug-compilation-dir=/__w/nvme-cli/nvme-cli/.build-ci -fcoverage-compilation-dir=/__w/nvme-cli/nvme-cli/.build-ci -resource-dir /usr/lib/llvm-19/lib/clang/19 -include /__w/nvme-cli/nvme-cli/.build-ci/nvme-config.h -I nvme.p -I . -I .. -I ccan -I ../ccan -I libnvme/src -I ../libnvme/src -I /usr/include/json-c -D _FILE_OFFSET_BITS=64 -D _GNU_SOURCE -U NDEBUG -internal-isystem /usr/lib/llvm-19/lib/clang/19/include -internal-isystem /usr/local/include -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/14/../../../../x86_64-linux-gnu/include -internal-externc-isystem /usr/include/x86_64-linux-gnu -internal-externc-isystem /include -internal-externc-isystem /usr/include -O3 -std=gnu99 -ferror-limit 19 -fgnuc-version=4.2.1 -fskip-odr-check-in-gmf -fcolor-diagnostics -vectorize-loops -vectorize-slp -analyzer-opt-analyze-headers -analyzer-output=html -faddrsig -D__GCC_HAVE_DWARF2_CFI_ASM=1 -o /__w/nvme-cli/nvme-cli/.build-ci/scan-results/2026-06-24-175442-590-1 -x c ../nvme-print-stdout-top.c
1// SPDX-License-Identifier: GPL-2.0-or-later
2/*
3 * Implements nvme top dashboard
4 *
5 * Copyright (c) 2026 Nilay Shroff, IBM
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 */
17
18#define IOPS_UNIT_NONE"" ""
19#define IOPS_UNIT_KB"k" "k"
20
21#define BW_UNIT_BYTES_PER_SEC"B/s" "B/s"
22#define BW_UNIT_KIB_PER_SEC"KiB/s" "KiB/s"
23#define BW_UNIT_MIB_PER_SEC"MiB/s" "MiB/s"
24
25#define BW_KIB1024 1024
26#define BW_MIB(1024 * 1024) (BW_KIB1024 * 1024)
27
28#include <stdio.h>
29#include <stddef.h>
30#include <libnvme.h>
31
32#include "nvme.h"
33#include "nvme-print.h"
34#include "common.h"
35#include "logging.h"
36#include "util/dashboard.h"
37#include "util/table.h"
38
39static double nvme_calc_util_percent(unsigned int ticks, double interval_ms)
40{
41 if (!interval_ms)
42 return 0;
43
44 return (ticks / interval_ms) * 100;
45}
46
47static double nvme_path_calc_util_percent(libnvme_path_t p, double interval_ms)
48{
49 unsigned int ticks;
50
51 ticks = libnvme_path_get_io_ticks(p);
52 return nvme_calc_util_percent(ticks, interval_ms);
53}
54
55static double nvme_ns_calc_util_percent(libnvme_ns_t n, double interval_ms)
56{
57 unsigned int ticks;
58
59 ticks = libnvme_ns_get_io_ticks(n);
60 return nvme_calc_util_percent(ticks, interval_ms);
61}
62
63static double nvme_calc_iops(unsigned long ios, double interval_ms)
64{
65 double interval_sec;
66
67 if (interval_ms < 1000)
68 return 0;
69
70 interval_sec = interval_ms / 1000;
71 return (ios / interval_sec);
72}
73
74static double nvme_path_calc_read_iops(libnvme_path_t p, double interval_ms)
75{
76 unsigned long read_ios;
77
78 read_ios = libnvme_path_get_read_ios(p);
79 return nvme_calc_iops(read_ios, interval_ms);
80}
81
82static double nvme_path_calc_write_iops(libnvme_path_t p, double interval_ms)
83{
84 unsigned long write_ios;
85
86 write_ios = libnvme_path_get_write_ios(p);
87 return nvme_calc_iops(write_ios, interval_ms);
88}
89
90static double nvme_ns_calc_read_iops(libnvme_ns_t n, double interval_ms)
91{
92 unsigned long read_ios;
93
94 read_ios = libnvme_ns_get_read_ios(n);
95 return nvme_calc_iops(read_ios, interval_ms);
96}
97
98static double nvme_ns_calc_write_iops(libnvme_ns_t n, double interval_ms)
99{
100 unsigned long write_ios;
101
102 write_ios = libnvme_ns_get_write_ios(n);
103 return nvme_calc_iops(write_ios, interval_ms);
104}
105
106static double nvme_calc_latency(unsigned long ticks, unsigned long ios)
107{
108 if (!ios)
109 return 0;
110
111 return ((double)ticks/ios);
112}
113
114static double nvme_path_calc_read_latency(libnvme_path_t p)
115{
116 unsigned int ticks;
117 unsigned long ios;
118
119 ticks = libnvme_path_get_read_ticks(p);
120 ios = libnvme_path_get_read_ios(p);
121
122 return nvme_calc_latency(ticks, ios);
123}
124
125static double nvme_path_calc_write_latency(libnvme_path_t p)
126{
127 unsigned int ticks;
128 unsigned long ios;
129
130 ticks = libnvme_path_get_write_ticks(p);
131 ios = libnvme_path_get_write_ios(p);
132
133 return nvme_calc_latency(ticks, ios);
134}
135
136static double nvme_ns_calc_read_latency(libnvme_ns_t n)
137{
138 unsigned int ticks;
139 unsigned long ios;
140
141 ticks = libnvme_ns_get_read_ticks(n);
142 ios = libnvme_ns_get_read_ios(n);
143
144 return nvme_calc_latency(ticks, ios);
145}
146
147static double nvme_ns_calc_write_latency(libnvme_ns_t n)
148{
149 unsigned int ticks;
150 unsigned long ios;
151
152 ticks = libnvme_ns_get_write_ticks(n);
153 ios = libnvme_ns_get_write_ios(n);
154
155 return nvme_calc_latency(ticks, ios);
156}
157
158static double nvme_calc_bandwidth(unsigned long long sectors,
159 double interval_ms)
160{
161 double bytes;
162 double sec;
163
164 if (interval_ms < 1000)
165 return 0;
166
167 sec = interval_ms / 1000;
168 bytes = (double)sectors * 512;
169 return (bytes / sec);
170}
171
172static double nvme_path_calc_read_bw(libnvme_path_t p, double interval_ms)
173{
174 unsigned long long sectors;
175
176 sectors = libnvme_path_get_read_sectors(p);
177 return nvme_calc_bandwidth(sectors, interval_ms);
178}
179
180static double nvme_path_calc_write_bw(libnvme_path_t p, double interval_ms)
181{
182 unsigned long long sectors;
183
184 sectors = libnvme_path_get_write_sectors(p);
185 return nvme_calc_bandwidth(sectors, interval_ms);
186}
187
188static double nvme_ns_calc_read_bw(libnvme_ns_t n, double interval_ms)
189{
190 unsigned long long sectors;
191
192 sectors = libnvme_ns_get_read_sectors(n);
193 return nvme_calc_bandwidth(sectors, interval_ms);
194}
195
196static double nvme_ns_calc_write_bw(libnvme_ns_t n, double interval_ms)
197{
198 unsigned long long sectors;
199
200 sectors = libnvme_ns_get_write_sectors(n);
201 return nvme_calc_bandwidth(sectors, interval_ms);
202}
203
204static int nvme_format_iops(double iops, char *buf, size_t size)
205{
206 char *unit;
207
208 if (iops < 1000)
209 unit = IOPS_UNIT_NONE"";
210 else {
211 iops /= 1000;
212 unit = IOPS_UNIT_KB"k";
213 }
214
215 return snprintf(buf, size, "%.2f%s", iops, unit);
216}
217
218static int nvme_format_bw(double bw, char *buf, size_t size)
219{
220 char *unit = "";
221
222 if (!bw)
223 goto out;
224
225 if (bw < BW_KIB1024)
226 unit = BW_UNIT_BYTES_PER_SEC"B/s";
227 else if (bw < BW_MIB(1024 * 1024)) {
228 bw /= BW_KIB1024;
229 unit = BW_UNIT_KIB_PER_SEC"KiB/s";
230 } else {
231 bw /= BW_MIB(1024 * 1024);
232 unit = BW_UNIT_MIB_PER_SEC"MiB/s";
233 }
234
235out:
236 return snprintf(buf, size, "%.2f%s", bw, unit);
237}
238
239static int nvme_format_lat(double lat, char *buf, size_t size)
240{
241 return snprintf(buf, size, "%.2f", lat);
242}
243
244static void nvme_ns_calc_aggr_stat(libnvme_ns_t n,
245 double *r_iops, double *w_iops,
246 double *r_bw, double *w_bw,
247 double *max_rlat, double *max_wlat,
248 double *max_util)
249{
250 double interval_ms, rlat, wlat, util;
251
252 interval_ms = libnvme_ns_get_stat_interval(n);
253 if (!interval_ms)
254 return;
255
256 *r_iops += nvme_ns_calc_read_iops(n, interval_ms);
257 *w_iops += nvme_ns_calc_write_iops(n, interval_ms);
258
259 *r_bw += nvme_ns_calc_read_bw(n, interval_ms);
260 *w_bw += nvme_ns_calc_write_bw(n, interval_ms);
261
262 rlat = nvme_ns_calc_read_latency(n);
263 if (rlat > *max_rlat)
264 *max_rlat = rlat;
265
266 wlat = nvme_ns_calc_write_latency(n);
267 if (wlat > *max_wlat)
268 *max_wlat = wlat;
269
270 util = nvme_ns_calc_util_percent(n, interval_ms);
271 if (util > *max_util)
272 *max_util = util;
273}
274
275static void nvme_path_calc_aggr_stat(libnvme_path_t p,
276 double *r_iops, double *w_iops,
277 double *r_bw, double *w_bw,
278 double *max_rlat, double *max_wlat,
279 double *max_util)
280{
281 double interval_ms, rlat, wlat, util;
282
283 interval_ms = libnvme_path_get_stat_interval(p);
284 if (!interval_ms)
285 return;
286
287 *r_iops += nvme_path_calc_read_iops(p, interval_ms);
288 *w_iops += nvme_path_calc_write_iops(p, interval_ms);
289
290 *r_bw += nvme_path_calc_read_bw(p, interval_ms);
291 *w_bw += nvme_path_calc_write_bw(p, interval_ms);
292
293 rlat = nvme_path_calc_read_latency(p);
294 if (rlat > *max_rlat)
295 *max_rlat = rlat;
296
297 wlat = nvme_path_calc_write_latency(p);
298 if (wlat > *max_wlat)
299 *max_wlat = wlat;
300
301 util = nvme_path_calc_util_percent(p, interval_ms);
302 if (util > *max_util)
303 *max_util = util;
304}
305
306static void nvme_ns_calc_stat(libnvme_ns_t n,
307 double *r_iops, double *w_iops,
308 double *r_lat, double *w_lat,
309 double *r_bw, double *w_bw,
310 double *util,
311 unsigned int *inflights)
312{
313 double interval_ms;
314
315 interval_ms = libnvme_ns_get_stat_interval(n);
316 if (!interval_ms)
317 return;
318
319 /* calculate R/W IOPS */
320 *r_iops = nvme_ns_calc_read_iops(n, interval_ms);
321 *w_iops = nvme_ns_calc_write_iops(n, interval_ms);
322
323 /* calculate R/W latency */
324 *r_lat = nvme_ns_calc_read_latency(n);
325 *w_lat = nvme_ns_calc_write_latency(n);
326
327 /* calculate R/W bandwidth */
328 *r_bw = nvme_ns_calc_read_bw(n, interval_ms);
329 *w_bw = nvme_ns_calc_write_bw(n, interval_ms);
330
331 /* get inflights counter */
332 *inflights = libnvme_ns_get_inflights(n);
333
334 /* calculate util percent */
335 *util = nvme_ns_calc_util_percent(n, interval_ms);
336}
337
338static void nvme_path_calc_stat(libnvme_path_t p,
339 double *r_iops, double *w_iops,
340 double *r_lat, double *w_lat,
341 double *r_bw, double *w_bw,
342 double *util,
343 unsigned int *inflights)
344{
345 double interval_ms;
346
347 interval_ms = libnvme_path_get_stat_interval(p);
348 if (!interval_ms)
349 return;
350
351 /* calculate R/W IOPS */
352 *r_iops = nvme_path_calc_read_iops(p, interval_ms);
353 *w_iops = nvme_path_calc_write_iops(p, interval_ms);
354
355 /* calculate R/W latency */
356 *r_lat = nvme_path_calc_read_latency(p);
357 *w_lat = nvme_path_calc_write_latency(p);
358
359 /* calculate R/W bandwidth */
360 *r_bw = nvme_path_calc_read_bw(p, interval_ms);
361 *w_bw = nvme_path_calc_write_bw(p, interval_ms);
362
363 /* get inflights counter */
364 *inflights = libnvme_path_get_inflights(p);
365
366 /* calculate util percent */
367 *util = nvme_path_calc_util_percent(p, interval_ms);
368}
369
370static bool_Bool stdout_top_nvme_ctrl_is_fabric(libnvme_ctrl_t c)
371{
372 if (strcmp(libnvme_ctrl_get_transport(c), "pcie"))
373 return true1;
374 else
375 return false0;
376}
377
378static bool_Bool stdout_top_print_ctrl_summary_tbl_filter(const char *name,
379 void *arg)
380{
381 libnvme_ctrl_t c;
382 libnvme_subsystem_t s = arg;
383 bool_Bool multipath = nvme_is_multipath(s);
384
385 if (!strcmp(name, "Paths")) {
386 if (!multipath)
387 return false0;
388 }
389
390 if (!strcmp(name, "Reconnects")) {
391 c = libnvme_subsystem_first_ctrl(s);
392 if (c) {
393 if (stdout_top_nvme_ctrl_is_fabric(c))
394 return true1;
395 else
396 return false0;
397 }
398 }
399
400 return true1;
401}
402
403static int stdout_top_print_path_health(FILE *stream, libnvme_subsystem_t s)
404{
405 int ret = 0;
406 int col, row;
407 libnvme_ns_t n;
408 libnvme_path_t p;
409 struct table *t;
410 struct table_column columns[] = {
411 {"NSPath", LEFT, AUTO_WIDTH2147483647},
412 {"ANAState", LEFT, AUTO_WIDTH2147483647},
413 {"Retries", LEFT, AUTO_WIDTH2147483647},
414 {"Failovers", LEFT, AUTO_WIDTH2147483647},
415 {"Errors", LEFT, AUTO_WIDTH2147483647}
416 };
417
418 t = table_create();
419 if (!t) {
420 nvme_show_error("Failed to init path health table\n")nvme_show_message(1, "Failed to init path health table\n");
421 return 1;
422 }
423
424 if (table_add_columns(t, columns, ARRAY_SIZE(columns)(sizeof(columns) / sizeof((columns)[0]))) < 0) {
425 nvme_show_error("Failed to add columns to path health table\n")nvme_show_message(1, "Failed to add columns to path health table\n"
)
;
426 ret = 1;
427 goto free_tbl;
428 }
429
430 fprintf(stream, "\n------------ Path Health -------------\n\n");
431 libnvme_subsystem_for_each_ns(s, n)for (n = libnvme_subsystem_first_ns(s); n != ((void*)0); n = libnvme_subsystem_next_ns
(s, n))
{
432 libnvme_namespace_for_each_path(n, p)for (p = libnvme_namespace_first_path(n); p != ((void*)0); p =
libnvme_namespace_next_path(n, p))
{
433
434 row = table_get_row_id(t);
435 if (row < 0) {
436 nvme_show_error("Failed to add row to path health table\n")nvme_show_message(1, "Failed to add row to path health table\n"
)
;
437 goto free_tbl;
438 }
439
440 col = -1;
441
442 table_set_value_str(t, ++col, row,
443 libnvme_path_get_name(p), LEFT);
444 table_set_value_str(t, ++col, row,
445 libnvme_path_get_ana_state(p), LEFT);
446 table_set_value_long(t, ++col, row,
447 libnvme_path_get_command_retry_count(p), LEFT);
448 table_set_value_long(t, ++col, row,
449 libnvme_path_get_multipath_failover_count(p), LEFT);
450 table_set_value_int(t, ++col, row,
451 libnvme_path_get_command_error_count(p), LEFT);
452
453 table_add_row(t, row);
454 }
455 }
456
457 table_print_stream(stream, t);
458free_tbl:
459 table_free(t);
460 return ret;
461}
462
463static int stdout_top_print_ctrl_summary(FILE *stream,
464 libnvme_subsystem_t s, bool_Bool multipath)
465{
466 int ret = 0;
467 int row, col, npaths;
468 libnvme_ctrl_t c;
469 libnvme_path_t p;
470 libnvme_ns_t n;
471 double max_util, max_rlat, max_wlat;
472 double r_iops, w_iops, r_bw, w_bw;
473 char r_bw_str[16], w_bw_str[16];
474 char r_iops_str[16], w_iops_str[16];
475 char r_clat_str[16], w_clat_str[16];
476 const char *node;
477 struct table *t;
478 bool_Bool is_fabric = false0;
479 struct table_column columns[] = {
480 {"Ctrl", LEFT, AUTO_WIDTH2147483647},
481 {"Paths", LEFT, AUTO_WIDTH2147483647},
482 {"Node", LEFT, AUTO_WIDTH2147483647},
483 {"Trtype", LEFT, AUTO_WIDTH2147483647},
484 {"Address", LEFT, AUTO_WIDTH2147483647},
485 {"State", LEFT, AUTO_WIDTH2147483647},
486 {"Resets", LEFT, AUTO_WIDTH2147483647},
487 {"Reconnects", LEFT, AUTO_WIDTH2147483647},
488 {"Errors", LEFT, AUTO_WIDTH2147483647},
489 {"r_IOPS", LEFT, 9},
490 {"w_IOPS", LEFT, 9},
491 {"r_clat", LEFT, 8},
492 {"w_clat", LEFT, 8},
493 {"r_bw", LEFT, 13},
494 {"w_bw", LEFT, 13},
495 {"Util%", LEFT, 6},
496 };
497
498 t = table_create();
499 if (!t) {
500 nvme_show_error("Failed to init ctrl summary table")nvme_show_message(1, "Failed to init ctrl summary table");
501 return 1;
502 }
503
504 if (table_add_columns_filter(t, columns, ARRAY_SIZE(columns)(sizeof(columns) / sizeof((columns)[0])),
505 stdout_top_print_ctrl_summary_tbl_filter, (void *)s) < 0) {
506 nvme_show_error("Failed to add columns to ctrl summary table")nvme_show_message(1, "Failed to add columns to ctrl summary table"
)
;
507 ret = 1;
508 goto free_tbl;
509 }
510
511 c = libnvme_subsystem_first_ctrl(s);
512 if (c)
513 is_fabric = stdout_top_nvme_ctrl_is_fabric(c);
514
515 fprintf(stream, "\n---------- Controller Summary --------\n\n");
516 libnvme_subsystem_for_each_ctrl(s, c)for (c = libnvme_subsystem_first_ctrl(s); c != ((void*)0); c =
libnvme_subsystem_next_ctrl(s, c))
{
517 npaths = 0;
518 r_iops = w_iops = 0;
519 r_bw = w_bw = 0;
520 max_util = max_rlat = max_wlat = 0;
521
522 row = table_get_row_id(t);
523 if (row < 0) {
524 nvme_show_error("Failed to add row to ctrl summary table")nvme_show_message(1, "Failed to add row to ctrl summary table"
)
;
525 ret = 1;
526 goto free_tbl;
527 }
528
529 if (multipath) {
530 libnvme_ctrl_for_each_path(c, p)for (p = libnvme_ctrl_first_path(c); p != ((void*)0); p = libnvme_ctrl_next_path
(c, p))
{
531
532 /* count num of paths per controller */
533 npaths++;
534
535 nvme_path_calc_aggr_stat(p,
536 &r_iops, &w_iops,
537 &r_bw, &w_bw,
538 &max_rlat, &max_wlat,
539 &max_util);
540 }
541 } else {
542 libnvme_ctrl_for_each_ns(c, n)for (n = libnvme_ctrl_first_ns(c); n != ((void*)0); n = libnvme_ctrl_next_ns
(c, n))
{
543 nvme_ns_calc_aggr_stat(n,
544 &r_iops, &w_iops,
545 &r_bw, &w_bw,
546 &max_rlat, &max_wlat,
547 &max_util);
548 }
549 }
550
551 nvme_format_iops(r_iops, r_iops_str, sizeof(r_iops_str));
552 nvme_format_iops(w_iops, w_iops_str, sizeof(w_iops_str));
553
554 nvme_format_bw(r_bw, r_bw_str, sizeof(r_bw_str));
555 nvme_format_bw(w_bw, w_bw_str, sizeof(w_bw_str));
556
557 nvme_format_lat(max_rlat, r_clat_str, sizeof(r_clat_str));
558 nvme_format_lat(max_wlat, w_clat_str, sizeof(w_clat_str));
559
560 node = libnvme_ctrl_get_numa_node(c);
561 if (!strcmp(node, "-1"))
562 node = "NUMA_NO_NODE";
563
564 col = -1;
565
566 table_set_value_str(t, ++col, row,
567 libnvme_ctrl_get_name(c), LEFT);
568 if (multipath)
569 table_set_value_int(t, ++col, row, npaths, LEFT);
570
571 table_set_value_str(t, ++col, row, node, LEFT);
572 table_set_value_str(t, ++col, row,
573 libnvme_ctrl_get_transport(c), LEFT);
574 table_set_value_str(t, ++col, row,
575 libnvme_ctrl_get_traddr(c), LEFT);
576 table_set_value_str(t, ++col, row,
577 libnvme_ctrl_get_state(c), LEFT);
578 table_set_value_long(t, ++col, row,
579 libnvme_ctrl_get_reset_count(c), LEFT);
580 if (is_fabric)
581 table_set_value_long(t, ++col, row,
582 libnvme_ctrl_get_reconnect_count(c), LEFT);
583
584 table_set_value_long(t, ++col, row,
585 libnvme_ctrl_get_command_error_count(c), LEFT);
586 table_set_value_str(t, ++col, row, r_iops_str, LEFT);
587 table_set_value_str(t, ++col, row, w_iops_str, LEFT);
588 table_set_value_str(t, ++col, row, r_clat_str, LEFT);
589 table_set_value_str(t, ++col, row, w_clat_str, LEFT);
590 table_set_value_str(t, ++col, row, r_bw_str, LEFT);
591 table_set_value_str(t, ++col, row, w_bw_str, LEFT);
592 table_set_value_double(t, ++col, row, max_util, LEFT);
593
594 table_add_row(t, row);
595 }
596
597 table_print_stream(stream, t);
598free_tbl:
599 table_free(t);
600 return ret;
601}
602
603static int stdout_top_print_ns_stat(FILE *stream, libnvme_subsystem_t s)
604{
605 int ret = 0;
606 libnvme_ns_t n;
607 libnvme_ctrl_t c;
608 int col, row;
609 unsigned int inflights;
610 double r_iops, w_iops, r_lat, w_lat, r_bw, w_bw, util;
611 char r_bw_str[16], w_bw_str[16];
612 char r_iops_str[16], w_iops_str[16];
613 char r_clat_str[16], w_clat_str[16];
614 struct table *t;
615 struct table_column columns[] = {
616 {"Namespace", LEFT, AUTO_WIDTH2147483647},
617 {"NSID", LEFT, AUTO_WIDTH2147483647},
618 {"Ctrl", LEFT, AUTO_WIDTH2147483647},
619 {"Retries", LEFT, AUTO_WIDTH2147483647},
620 {"Errors", LEFT, AUTO_WIDTH2147483647},
621 {"r_IOPS", LEFT, 9},
622 {"w_IOPS", LEFT, 9},
623 {"r_clat", LEFT, 8},
624 {"w_clat", LEFT, 8},
625 {"r_bw", LEFT, 13},
626 {"w_bw", LEFT, 13},
627 {"Inflights", LEFT, AUTO_WIDTH2147483647},
628 {"Util%", LEFT, 6},
629 };
630
631 t = table_create();
632 if (!t) {
633 nvme_show_error("Failed to init ns stat table\n")nvme_show_message(1, "Failed to init ns stat table\n");
634 return 1;
635 }
636
637 if (table_add_columns(t, columns, ARRAY_SIZE(columns)(sizeof(columns) / sizeof((columns)[0]))) < 0) {
638 nvme_show_error("Failed to add columns to ns stat table\n")nvme_show_message(1, "Failed to add columns to ns stat table\n"
)
;
639 ret = 1;
640 goto free_tbl;
641 }
642
643 fprintf(stream, "----------- Namespace Stat -----------\n\n");
644 libnvme_subsystem_for_each_ctrl(s, c)for (c = libnvme_subsystem_first_ctrl(s); c != ((void*)0); c =
libnvme_subsystem_next_ctrl(s, c))
{
645 libnvme_ctrl_for_each_ns(c, n)for (n = libnvme_ctrl_first_ns(c); n != ((void*)0); n = libnvme_ctrl_next_ns
(c, n))
{
646 r_iops = r_lat = r_bw = 0;
647 w_iops = w_lat = w_bw = 0;
648 util = inflights = 0;
649
650 nvme_ns_calc_stat(n,
651 &r_iops, &w_iops,
652 &r_lat, &w_lat,
653 &r_bw, &w_bw,
654 &util, &inflights);
655
656 nvme_format_iops(r_iops, r_iops_str,
657 sizeof(r_iops_str));
658 nvme_format_iops(w_iops, w_iops_str,
659 sizeof(w_iops_str));
660
661 nvme_format_bw(r_bw, r_bw_str, sizeof(r_bw_str));
662 nvme_format_bw(w_bw, w_bw_str, sizeof(w_bw_str));
663
664 nvme_format_lat(r_lat, r_clat_str, sizeof(r_clat_str));
665 nvme_format_lat(w_lat, w_clat_str, sizeof(w_clat_str));
666
667 row = table_get_row_id(t);
668 if (row < 0) {
669 nvme_show_error("Failed to add row to ns stat table\n")nvme_show_message(1, "Failed to add row to ns stat table\n");
670 ret = 1;
671 goto free_tbl;
672 }
673
674 col = -1;
675
676 table_set_value_str(t, ++col, row,
677 libnvme_ns_get_name(n), LEFT);
678 table_set_value_int(t, ++col, row,
679 libnvme_ns_get_nsid(n), LEFT);
680 table_set_value_str(t, ++col, row,
681 libnvme_ctrl_get_name(c), LEFT);
682 table_set_value_long(t, ++col, row,
683 libnvme_ns_get_command_retry_count(n), LEFT);
684 table_set_value_long(t, ++col, row,
685 libnvme_ns_get_command_error_count(n), LEFT);
686 table_set_value_str(t, ++col, row, r_iops_str, LEFT);
687 table_set_value_str(t, ++col, row, w_iops_str, LEFT);
688 table_set_value_str(t, ++col, row, r_clat_str, LEFT);
689 table_set_value_str(t, ++col, row, w_clat_str, LEFT);
690 table_set_value_str(t, ++col, row, r_bw_str, LEFT);
691 table_set_value_str(t, ++col, row, w_bw_str, LEFT);
692 table_set_value_unsigned(t, ++col, row, inflights,
693 LEFT);
694 table_set_value_double(t, ++col, row, util, LEFT);
695
696 table_add_row(t, row);
697 }
698 }
699
700 table_print_stream(stream, t);
701free_tbl:
702 table_free(t);
703 return ret;
704}
705
706static int stdout_top_print_nshead_stat(FILE *stream, libnvme_subsystem_t s)
707{
708 int ret = 0;
709 libnvme_ns_t n;
710 libnvme_path_t p;
711 double r_iops, w_iops, r_lat, w_lat, r_bw, w_bw, util;
712 unsigned int inflights;
713 int col, row, npaths;
714 char r_iops_str[16], w_iops_str[16];
715 char r_clat_str[16], w_clat_str[16];
716 char r_bw_str[16], w_bw_str[16];
717 struct table *t;
718 struct table_column columns[] = {
719 {"NSHead", LEFT, AUTO_WIDTH2147483647},
720 {"NSID", LEFT, AUTO_WIDTH2147483647},
721 {"Paths", LEFT, AUTO_WIDTH2147483647},
722 {"Requeue-IO", LEFT, AUTO_WIDTH2147483647},
723 {"Fail-IO", LEFT, AUTO_WIDTH2147483647},
724 {"r_IOPS", LEFT, 9},
725 {"w_IOPS", LEFT, 9},
726 {"r_clat", LEFT, 8},
727 {"w_clat", LEFT, 8},
728 {"r_bw", LEFT, 13},
729 {"w_bw", LEFT, 13},
730 {"Inflights", LEFT, AUTO_WIDTH2147483647},
731 {"Util%", LEFT, 6},
732 };
733
734 t = table_create();
735 if (!t) {
736 nvme_show_error("Failed to init nshead stat table\n")nvme_show_message(1, "Failed to init nshead stat table\n");
737 return 1;
738 }
739
740 if (table_add_columns(t, columns, ARRAY_SIZE(columns)(sizeof(columns) / sizeof((columns)[0]))) < 0) {
741 nvme_show_error("Failed to add columns to nshead stat table\n")nvme_show_message(1, "Failed to add columns to nshead stat table\n"
)
;
742 ret = 1;
743 goto free_tbl;
744 }
745
746 fprintf(stream, "------------ NSHead Stat -------------\n\n");
747 libnvme_subsystem_for_each_ns(s, n)for (n = libnvme_subsystem_first_ns(s); n != ((void*)0); n = libnvme_subsystem_next_ns
(s, n))
{
748 npaths = 0;
749 r_iops = r_lat = r_bw = 0;
750 w_iops = w_lat = w_bw = 0;
751 util = inflights = 0;
752
753 nvme_ns_calc_stat(n,
754 &r_iops, &w_iops,
755 &r_lat, &w_lat,
756 &r_bw, &w_bw,
757 &util, &inflights);
758
759 nvme_format_iops(r_iops, r_iops_str, sizeof(r_iops_str));
760 nvme_format_iops(w_iops, w_iops_str, sizeof(w_iops_str));
761
762 nvme_format_bw(r_bw, r_bw_str, sizeof(r_bw_str));
763 nvme_format_bw(w_bw, w_bw_str, sizeof(w_bw_str));
764
765 nvme_format_lat(r_lat, r_clat_str, sizeof(r_clat_str));
766 nvme_format_lat(w_lat, w_clat_str, sizeof(w_clat_str));
767
768 libnvme_namespace_for_each_path(n, p)for (p = libnvme_namespace_first_path(n); p != ((void*)0); p =
libnvme_namespace_next_path(n, p))
769 npaths++;
770
771 row = table_get_row_id(t);
772 if (row < 0) {
773 nvme_show_error("Failed to add row to nshead stat table\n")nvme_show_message(1, "Failed to add row to nshead stat table\n"
)
;
774 ret = 1;
775 goto free_tbl;
776 }
777
778 col = -1;
779
780 table_set_value_str(t, ++col, row, libnvme_ns_get_name(n),
781 LEFT);
782 table_set_value_int(t, ++col, row, libnvme_ns_get_nsid(n),
783 LEFT);
784 table_set_value_int(t, ++col, row, npaths, LEFT);
785 table_set_value_long(t, ++col, row,
786 libnvme_ns_get_io_requeue_no_usable_path_count(n), LEFT);
787 table_set_value_long(t, ++col, row,
788 libnvme_ns_get_io_fail_no_available_path_count(n), LEFT);
789 table_set_value_str(t, ++col, row, r_iops_str, LEFT);
790 table_set_value_str(t, ++col, row, w_iops_str, LEFT);
791 table_set_value_str(t, ++col, row, r_clat_str, LEFT);
792 table_set_value_str(t, ++col, row, w_clat_str, LEFT);
793 table_set_value_str(t, ++col, row, r_bw_str, LEFT);
794 table_set_value_str(t, ++col, row, w_bw_str, LEFT);
795 table_set_value_unsigned(t, ++col, row, inflights, LEFT);
796 table_set_value_double(t, ++col, row, util, LEFT);
797
798 table_add_row(t, row);
799 }
800
801 table_print_stream(stream, t);
802free_tbl:
803 table_free(t);
804 return ret;
805}
806
807static int stdout_top_print_path_perf(FILE *stream, libnvme_subsystem_t s)
808{
809 int ret = 0;
810 libnvme_ns_t n;
811 libnvme_path_t p;
812 libnvme_ctrl_t c;
813 unsigned int inflights;
814 int row, col;
815 double util, r_iops, w_iops, r_lat, w_lat, r_bw, w_bw;
816 char r_iops_str[16], w_iops_str[16];
817 char r_clat_str[16], w_clat_str[16];
818 char r_bw_str[16], w_bw_str[16];
819 bool_Bool first;
820 struct table *t;
821 const char *iopolicy = libnvme_subsystem_get_iopolicy(s);
822 struct table_column columns[] = {
823 {"NSHead", LEFT, AUTO_WIDTH2147483647},
824 {"NSID", LEFT, AUTO_WIDTH2147483647},
825 {"NSPath", LEFT, AUTO_WIDTH2147483647},
826 {"Nodes", LEFT, AUTO_WIDTH2147483647},
827 {"Qdepth", LEFT, AUTO_WIDTH2147483647},
828 {"Ctrl", LEFT, AUTO_WIDTH2147483647},
829 {"r_IOPS", LEFT, 9},
830 {"w_IOPS", LEFT, 9},
831 {"r_clat", LEFT, 8},
832 {"w_clat", LEFT, 8},
833 {"r_bw", LEFT, 13},
834 {"w_bw", LEFT, 13},
835 {"Inflights", LEFT, AUTO_WIDTH2147483647},
836 {"Util%", LEFT, 6},
837 };
838
839 t = table_create();
840 if (!t) {
841 nvme_show_error("Failed to init path perf table")nvme_show_message(1, "Failed to init path perf table");
842 return 1;
843 }
844
845 if (table_add_columns_filter(t, columns, ARRAY_SIZE(columns)(sizeof(columns) / sizeof((columns)[0])),
846 subsystem_iopolicy_filter, (void *)s) < 0) {
847 nvme_show_error("Failed to add columns to path perf table")nvme_show_message(1, "Failed to add columns to path perf table"
)
;
848 ret = 1;
849 goto free_tbl;
850 }
851
852 fprintf(stream, "\n---------- Path Performance ----------\n\n");
853 libnvme_subsystem_for_each_ns(s, n)for (n = libnvme_subsystem_first_ns(s); n != ((void*)0); n = libnvme_subsystem_next_ns
(s, n))
{
854 first = true1;
855 libnvme_namespace_for_each_path(n, p)for (p = libnvme_namespace_first_path(n); p != ((void*)0); p =
libnvme_namespace_next_path(n, p))
{
856 r_iops = r_lat = r_bw = 0;
857 w_iops = w_lat = w_bw = 0;
858 util = inflights = 0;
859
860 nvme_path_calc_stat(p,
861 &r_iops, &w_iops,
862 &r_lat, &w_lat,
863 &r_bw, &w_bw,
864 &util, &inflights);
865
866 nvme_format_iops(r_iops, r_iops_str,
867 sizeof(r_iops_str));
868 nvme_format_iops(w_iops, w_iops_str,
869 sizeof(w_iops_str));
870
871 nvme_format_bw(r_bw, r_bw_str, sizeof(r_bw_str));
872 nvme_format_bw(w_bw, w_bw_str, sizeof(w_bw_str));
873
874 nvme_format_lat(r_lat, r_clat_str, sizeof(r_clat_str));
875 nvme_format_lat(w_lat, w_clat_str, sizeof(w_clat_str));
876
877 /* get controller associated with the path */
878 c = libnvme_path_get_ctrl(p);
879
880 row = table_get_row_id(t);
881 if (row < 0) {
882 nvme_show_error("Failed to add row to path perf table")nvme_show_message(1, "Failed to add row to path perf table");
883 ret = 1;
884 goto free_tbl;
885 }
886
887 /*
888 * For the first row we print actual NSHead name,
889 * however, for the subsequent rows we print "arrow"
890 * ("-->") symbol for NSHead. This "arrow" style makes
891 * it visually obvious that subsequent entries (if
892 * present) are a path under the first NSHead.
893 */
894 col = -1;
895
896 if (first) {
897 table_set_value_str(t, ++col, row,
898 libnvme_ns_get_name(n), LEFT);
899 first = false0;
900 } else
901 table_set_value_str(t, ++col, row,
902 "-->", CENTERED);
903
904 table_set_value_int(t, ++col, row,
905 libnvme_ns_get_nsid(n), CENTERED);
906 table_set_value_str(t, ++col, row,
907 libnvme_path_get_name(p), LEFT);
908
909 if (!strcmp(iopolicy, "numa"))
910 table_set_value_str(t, ++col, row,
911 libnvme_path_get_numa_nodes(p), CENTERED);
912 else if (!strcmp(iopolicy, "queue-depth"))
913 table_set_value_int(t, ++col, row,
914 libnvme_path_get_queue_depth(p), CENTERED);
915
916 table_set_value_str(t, ++col, row,
917 libnvme_ctrl_get_name(c), LEFT);
918 table_set_value_str(t, ++col, row, r_iops_str, LEFT);
919 table_set_value_str(t, ++col, row, w_iops_str, LEFT);
920 table_set_value_str(t, ++col, row, r_clat_str, LEFT);
921 table_set_value_str(t, ++col, row, w_clat_str, LEFT);
922 table_set_value_str(t, ++col, row, r_bw_str, LEFT);
923 table_set_value_str(t, ++col, row, w_bw_str, LEFT);
924 table_set_value_unsigned(t, ++col, row,
925 inflights, LEFT);
926 table_set_value_double(t, ++col, row, util, LEFT);
927
928 table_add_row(t, row);
929 }
930 }
931 table_print_stream(stream, t);
932free_tbl:
933 table_free(t);
934 return ret;
935}
936
937static void stdout_top_print_subsys_topology_config(FILE *stream,
938 libnvme_subsystem_t s)
939{
940 int len = strlen(libnvme_subsystem_get_name(s));
941
942 fprintf(stream, "%s - NQN=%s\n", libnvme_subsystem_get_name(s),
943 libnvme_subsystem_get_subsysnqn(s));
944 fprintf(stream, "%*s hostnqn=%s\n", len, " ",
945 libnvme_host_get_hostnqn(libnvme_subsystem_get_host(s)));
946 fprintf(stream, "%*s iopolicy=%s\n", len, " ",
947 libnvme_subsystem_get_iopolicy(s));
948
949 fprintf(stream, "%*s model=%s\n", len, " ",
950 libnvme_subsystem_get_model(s));
951 fprintf(stream, "%*s serial=%s\n", len, " ",
952 libnvme_subsystem_get_serial(s));
953 fprintf(stream, "%*s firmware=%s\n", len, " ",
954 libnvme_subsystem_get_firmware(s));
955 fprintf(stream, "%*s type=%s\n", len, " ",
956 libnvme_subsystem_get_subsystype(s));
957
958 fprintf(stream, "\n");
959}
960
961static int stdout_top_update_stat(libnvme_subsystem_t s)
962{
963 libnvme_ctrl_t c;
964 libnvme_ns_t n;
965 libnvme_path_t p;
966 int ret;
967
968 if (nvme_is_multipath(s)) {
969 libnvme_subsystem_for_each_ns(s, n)for (n = libnvme_subsystem_first_ns(s); n != ((void*)0); n = libnvme_subsystem_next_ns
(s, n))
{
970 ret = libnvme_ns_update_stat(n, true1);
971 if (ret < 0) {
972 nvme_show_error("Failed to update namespace stat")nvme_show_message(1, "Failed to update namespace stat");
973 return ret;
974 }
975
976 libnvme_namespace_for_each_path(n, p)for (p = libnvme_namespace_first_path(n); p != ((void*)0); p =
libnvme_namespace_next_path(n, p))
{
977 ret = libnvme_path_update_stat(p, true1);
978 if (ret < 0) {
979 nvme_show_error("Failed to update path stat")nvme_show_message(1, "Failed to update path stat");
980 return ret;
981 }
982 }
983 }
984 } else {
985 libnvme_subsystem_for_each_ctrl(s, c)for (c = libnvme_subsystem_first_ctrl(s); c != ((void*)0); c =
libnvme_subsystem_next_ctrl(s, c))
{
986 libnvme_ctrl_for_each_ns(c, n)for (n = libnvme_ctrl_first_ns(c); n != ((void*)0); n = libnvme_ctrl_next_ns
(c, n))
{
987 ret = libnvme_ns_update_stat(n, true1);
988 if (ret < 0) {
989 nvme_show_error("Failed to update namespace stat")nvme_show_message(1, "Failed to update namespace stat");
990 return ret;
991 }
992 }
993 }
994 }
995
996 return 0;
997}
998
999static void stdout_top_reset_stat(libnvme_subsystem_t s)
1000{
1001 libnvme_ctrl_t c;
1002 libnvme_ns_t n;
1003 libnvme_path_t p;
1004
1005 if (nvme_is_multipath(s)) {
1006 libnvme_subsystem_for_each_ns(s, n)for (n = libnvme_subsystem_first_ns(s); n != ((void*)0); n = libnvme_subsystem_next_ns
(s, n))
{
1007 libnvme_ns_reset_stat(n);
1008
1009 libnvme_namespace_for_each_path(n, p)for (p = libnvme_namespace_first_path(n); p != ((void*)0); p =
libnvme_namespace_next_path(n, p))
1010 libnvme_path_reset_stat(p);
1011 }
1012 } else {
1013 libnvme_subsystem_for_each_ctrl(s, c)for (c = libnvme_subsystem_first_ctrl(s); c != ((void*)0); c =
libnvme_subsystem_next_ctrl(s, c))
{
1014
1015 libnvme_ctrl_for_each_ns(c, n)for (n = libnvme_ctrl_first_ns(c); n != ((void*)0); n = libnvme_ctrl_next_ns
(c, n))
1016 libnvme_ns_reset_stat(n);
1017 }
1018 }
1019}
1020
1021static int stdout_top_print_subsys_topology(struct dashboard_ctx *db_ctx,
1022 FILE *stream, libnvme_subsystem_t s)
1023{
1024 int ret = 0;
1025 bool_Bool multipath = nvme_is_multipath(s);
1026
1027 ret = stdout_top_update_stat(s);
1028 if (ret)
1029 return ret;
1030
1031 stdout_top_print_subsys_topology_config(stream, s);
1032
1033 if (multipath) {
1034 ret = stdout_top_print_nshead_stat(stream, s);
1035 if (ret)
1036 return ret;
1037
1038 ret = stdout_top_print_path_perf(stream, s);
1039 if (ret)
1040 return ret;
1041
1042 ret = stdout_top_print_path_health(stream, s);
1043 if (ret)
1044 return ret;
1045 } else {
1046 ret = stdout_top_print_ns_stat(stream, s);
1047 if (ret)
1048 return ret;
1049 }
1050
1051 ret = stdout_top_print_ctrl_summary(stream, s, multipath);
1052
1053 return ret;
1054}
1055
1056static void stdout_top_print_subsys_topology_header(
1057 struct dashboard_ctx *db_ctx, FILE *stream)
1058{
1059 fprintf(stream, "---- nvme-top - Refresh: %d Second ----\n",
1060 dashboard_get_interval(db_ctx));
1061
1062 dashboard_set_header_rows(db_ctx, 1);
1063
1064 /* highlight the header row */
1065 dashboard_set_header_row_reverse(db_ctx, 0);
1066}
1067
1068static void stdout_top_print_subsys_topology_footer(
1069 struct dashboard_ctx *db_ctx, FILE *stream)
1070{
1071 fprintf(stream, "\n--------------------------------------\n");
1072 fprintf(stream, "[ESC to go back to the previous screen, q to quit]\n");
1073
1074 dashboard_set_footer_rows(db_ctx, 3);
1075
1076 /* hightligh the last footer row */
1077 dashboard_set_footer_row_reverse(db_ctx, 2);
1078}
1079
1080static struct libnvme_global_ctx *stdout_top_rescan_topology(void)
1081{
1082 struct libnvme_global_ctx *ctx;
1083
1084 ctx = libnvme_create_global_ctx(stdoutstdout, log_level);
1085 if (!ctx) {
1086 nvme_show_error("Failed to create global context")nvme_show_message(1, "Failed to create global context");
1087 return NULL((void*)0);
1088 }
1089
1090 if (libnvme_scan_topology(ctx, NULL((void*)0), NULL((void*)0))) {
1091 libnvme_free_global_ctx(ctx);
1092 nvme_show_error("Failed to scan topology")nvme_show_message(1, "Failed to scan topology");
1093 return NULL((void*)0);
1094 }
1095
1096 return ctx;
1097}
1098
1099static libnvme_subsystem_t stdout_top_search_subsystem(
1100 struct libnvme_global_ctx *ctx, const char *subsys_name)
1101{
1102 libnvme_host_t h;
1103 libnvme_subsystem_t s;
1104
1105 libnvme_for_each_host(ctx, h)for (h = libnvme_first_host(ctx); h != ((void*)0); h = libnvme_next_host
(ctx, h))
{
1106 libnvme_for_each_subsystem(h, s)for (s = libnvme_first_subsystem(h); s != ((void*)0); s = libnvme_next_subsystem
(h, s))
{
1107 if (!strcmp(libnvme_subsystem_get_name(s), subsys_name))
1108 return s;
1109 }
1110 }
1111
1112 return NULL((void*)0);
1113}
1114
1115static libnvme_subsystem_t *stdout_top_build_subsys_arr(
1116 struct libnvme_global_ctx *ctx, int *num_subsys)
1117{
1118 libnvme_host_t h;
1119 libnvme_subsystem_t s;
1120 libnvme_subsystem_t *subsys_arr;
1121 int subsys_idx = 0;
1122 int n = 0;
1123
1124 libnvme_for_each_host(ctx, h)for (h = libnvme_first_host(ctx); h != ((void*)0); h = libnvme_next_host
(ctx, h))
1125 libnvme_for_each_subsystem(h, s)for (s = libnvme_first_subsystem(h); s != ((void*)0); s = libnvme_next_subsystem
(h, s))
1126 n++;
1127 if (!n) {
1128 nvme_show_error("Can't find any NVMe subsystem on the host\n")nvme_show_message(1, "Can't find any NVMe subsystem on the host\n"
)
;
1129 return NULL((void*)0);
1130 }
1131
1132 subsys_arr = calloc(n, sizeof(libnvme_subsystem_t));
1133 if (!subsys_arr) {
1134 nvme_show_error("Failed to allocate memory for subsys array\n")nvme_show_message(1, "Failed to allocate memory for subsys array\n"
)
;
1135 return NULL((void*)0);
1136 }
1137
1138 libnvme_for_each_host(ctx, h)for (h = libnvme_first_host(ctx); h != ((void*)0); h = libnvme_next_host
(ctx, h))
{
1139 libnvme_for_each_subsystem(h, s)for (s = libnvme_first_subsystem(h); s != ((void*)0); s = libnvme_next_subsystem
(h, s))
1140 subsys_arr[subsys_idx++] = s;
1141 }
1142
1143 *num_subsys = n;
1144 return subsys_arr;
1145}
1146
1147static int stdout_top_find_subsys_by_name(libnvme_subsystem_t *subsys_arr,
1148 int num_subsys, const char *subsys_name)
1149{
1150 int idx;
1151 libnvme_subsystem_t s;
1152
1153 for (idx = 0; idx < num_subsys; idx++) {
1154 s = subsys_arr[idx];
1155
1156 if (!strcmp(libnvme_subsystem_get_name(s), subsys_name))
1157 return idx;
1158 }
1159
1160 return -1;
1161}
1162
1163/*
1164 * Draws subsys topology screen of susbystem @s
1165 * Returns: 0 if ESC key is pressed or needs to draw subsystem selection screen
1166 * 1 if 'q' is pressed or in case of error
1167 */
1168static int stdout_top_draw_subsys_topology_screen(struct dashboard_ctx *db_ctx,
1169 FILE *stream, libnvme_subsystem_t _s)
1170{
1171 __cleanup_nvme_global_ctx__attribute__((cleanup(cleanup_nvme_global_ctx))) struct libnvme_global_ctx *ctx = NULL((void*)0);
1172 enum event_type event;
1173 int ret, scroll = 0;
1174 int data_start, data_rows;
1175 libnvme_subsystem_t s = NULL((void*)0);
1176
1177 ctx = stdout_top_rescan_topology();
1178 if (!ctx)
1179 return 1; /* force quit */
1180
1181 s = stdout_top_search_subsystem(ctx, libnvme_subsystem_get_name(_s));
1182 if (!s)
1183 return 0; /* draw subsys selection screen */
1184
1185 while (1) {
1186 stdout_top_print_subsys_topology_header(db_ctx, stream);
1187 ret = stdout_top_print_subsys_topology(db_ctx, stream, s);
1188 if (ret)
1189 break;
1190 stdout_top_print_subsys_topology_footer(db_ctx, stream);
1191
1192draw:
1193 ret = dashboard_draw_frame(db_ctx, scroll);
1194 if (ret)
1195 break;
1196wait_for_event:
1197 event = dashboard_wait_for_event(db_ctx);
1198 if (event == EVENT_TYPE_KEY_ESC) {
1199 ret = 0;
1200 dashboard_reset(db_ctx);
1201 break;
1202 } else if (event == EVENT_TYPE_KEY_UP) {
1203 data_start = dashboard_get_data_start(db_ctx);
1204 /*
1205 * If we don't move past the first data row by shifting
1206 * one data row up then do so, otherwise ignore the key
1207 * press.
1208 */
1209 if (data_start - 1 >= 0) {
1210 dashboard_set_data_start(db_ctx,
1211 data_start - 1);
1212 scroll = 1;
1213 goto draw;
1214 }
1215 goto wait_for_event;
1216 } else if (event == EVENT_TYPE_KEY_DOWN) {
1217 data_start = dashboard_get_data_start(db_ctx);
1218 data_rows = dashboard_get_data_rows(db_ctx);
1219 /*
1220 * If we don't move past the max data rows shifting one
1221 * row down then do so, otherwise ignore the key press.
1222 */
1223 if (data_start + 1 < data_rows) {
1224 dashboard_set_data_start(db_ctx,
1225 data_start + 1);
1226 scroll = 1;
1227 goto draw;
1228 }
1229 goto wait_for_event;
1230 } else if (event == EVENT_TYPE_TIMEOUT) { /* screen timed out */
1231 scroll = 0;
1232 } else if (event == EVENT_TYPE_KEY_QUIT ||
1233 event == EVENT_TYPE_ERROR) {
1234 ret = 1;
1235 break;
1236 } else if (event == EVENT_TYPE_NVME_UEVENT) {
1237 /* free old ctx */
1238 libnvme_free_global_ctx(ctx);
1239 ctx = stdout_top_rescan_topology();
1240 if (!ctx) {
1241 ret = 1; /* force quit */
1242 break;
1243 }
1244
1245 s = stdout_top_search_subsystem(ctx,
1246 libnvme_subsystem_get_name(_s));
1247 if (!s) {
1248 ret = 0; /* draw subsys selection screen */
1249 break;
1250 }
1251 scroll = 0;
1252 } else if (event == EVENT_TYPE_SIGWINCH) {
1253 /*
1254 * Window size would have changed so re-draw the subsys
1255 * topology screen.
1256 */
1257 scroll = 0;
1258 } /* else unknown event, ignore */
1259 }
1260
1261 return ret;
1262}
1263
1264static int stdout_top_draw_subsys_screen(struct dashboard_ctx *db_ctx,
1265 FILE *stream, libnvme_subsystem_t *subsys_arr, int num_subsys)
1266{
1267 int ret = 0;
1268 libnvme_subsystem_t s;
1269 libnvme_ctrl_t c;
1270 libnvme_ns_t n;
1271 libnvme_path_t p;
1272 int i, row, col, num_ns, num_path, num_ctrl;
1273 double r_iops, w_iops;
1274 double r_bw, w_bw;
1275 double max_rlat, max_wlat, max_util;
1276 char r_bw_str[16], w_bw_str[16];
1277 char r_iops_str[16], w_iops_str[16];
1278 char r_clat_str[16], w_clat_str[16];
1279 char *iopolicy;
1280 struct table *t;
1281 struct table_column columns[] = {
1282 {"Subsystem", LEFT, AUTO_WIDTH2147483647},
1283 {"Namespaces", LEFT, AUTO_WIDTH2147483647},
1284 {"Paths", LEFT, AUTO_WIDTH2147483647},
1285 {"Ctrls", LEFT, AUTO_WIDTH2147483647},
1286 {"IOPolicy", LEFT, AUTO_WIDTH2147483647},
1287 {"r_IOPS", LEFT, 9},
1288 {"w_IOPS", LEFT, 9},
1289 {"r_clat", LEFT, 8},
1290 {"w_clat", LEFT, 8},
1291 {"r_bw", LEFT, 13},
1292 {"w_bw", LEFT, 13},
1293 {"Util%", LEFT, 6},
1294 };
1295
1296 fprintf(stream, "---- nvme-top - Refresh: %d Second ----\n",
1297 dashboard_get_interval(db_ctx));
1298 fprintf(stream, "\n--------- Subsystem Summary ----------\n\n");
1299
1300 t = table_create();
1301 if (!t) {
1302 nvme_show_error("Failed to init subsys screen table\n")nvme_show_message(1, "Failed to init subsys screen table\n");
1303 return -1;
1304 }
1305
1306 if (table_add_columns(t, columns, ARRAY_SIZE(columns)(sizeof(columns) / sizeof((columns)[0]))) < 0) {
1307 nvme_show_error("Failed to add columns to subsys screen table\n")nvme_show_message(1, "Failed to add columns to subsys screen table\n"
)
;
1308 ret = -1;
1309 goto free_tbl;
1310 }
1311 /*
1312 * Header row count is calculated manually. The first row displays the
1313 * refresh interval, followed by an empty row. The third row displays
1314 * the heading followed by another empty row. The fifth row is for
1315 * displaying table columns and then another row for dashes underneath
1316 * the table columns.
1317 */
1318 dashboard_set_header_rows(db_ctx, 6);
1319
1320 /* highlight the first header row */
1321 dashboard_set_header_row_reverse(db_ctx, 0);
1322
1323 for (i = 0; i < num_subsys; i++) {
1324 s = subsys_arr[i];
1325 num_ctrl = num_ns = num_path = 0;
1326 r_iops = w_iops = 0;
1327 r_bw = w_bw = 0;
1328 max_rlat = max_wlat = 0;
1329 max_util = 0;
1330 iopolicy = libnvme_subsystem_get_iopolicy(s);
1331
1332 libnvme_subsystem_for_each_ctrl(s, c)for (c = libnvme_subsystem_first_ctrl(s); c != ((void*)0); c =
libnvme_subsystem_next_ctrl(s, c))
1333 num_ctrl++;
1334
1335 ret = stdout_top_update_stat(s);
1336 if (ret)
1337 goto free_tbl;
1338
1339 if (nvme_is_multipath(s)) {
1340 libnvme_subsystem_for_each_ns(s, n)for (n = libnvme_subsystem_first_ns(s); n != ((void*)0); n = libnvme_subsystem_next_ns
(s, n))
{
1341 num_ns++;
1342
1343 libnvme_namespace_for_each_path(n, p)for (p = libnvme_namespace_first_path(n); p != ((void*)0); p =
libnvme_namespace_next_path(n, p))
1344 num_path++;
1345
1346 nvme_ns_calc_aggr_stat(n,
1347 &r_iops, &w_iops,
1348 &r_bw, &w_bw,
1349 &max_rlat, &max_wlat,
1350 &max_util);
1351 }
1352 } else {
1353 libnvme_subsystem_for_each_ctrl(s, c)for (c = libnvme_subsystem_first_ctrl(s); c != ((void*)0); c =
libnvme_subsystem_next_ctrl(s, c))
{
1354 libnvme_ctrl_for_each_ns(c, n)for (n = libnvme_ctrl_first_ns(c); n != ((void*)0); n = libnvme_ctrl_next_ns
(c, n))
{
1355 num_ns++;
1356
1357 nvme_ns_calc_aggr_stat(n,
1358 &r_iops, &w_iops,
1359 &r_bw, &w_bw,
1360 &max_rlat, &max_wlat,
1361 &max_util);
1362 }
1363 }
1364 }
1365
1366 nvme_format_iops(r_iops, r_iops_str, sizeof(r_iops_str));
1367 nvme_format_iops(w_iops, w_iops_str, sizeof(w_iops_str));
1368
1369 nvme_format_bw(r_bw, r_bw_str, sizeof(r_bw_str));
1370 nvme_format_bw(w_bw, w_bw_str, sizeof(w_bw_str));
1371
1372 nvme_format_lat(max_rlat, r_clat_str, sizeof(r_clat_str));
1373 nvme_format_lat(max_wlat, w_clat_str, sizeof(w_clat_str));
1374
1375 row = table_get_row_id(t);
1376 if (row < 0) {
1377 nvme_show_error("Failed to add row to subsys screen table\n")nvme_show_message(1, "Failed to add row to subsys screen table\n"
)
;
1378 ret = -1;
1379 goto free_tbl;
1380 }
1381
1382 col = -1;
1383
1384 table_set_value_str(t, ++col, row,
1385 libnvme_subsystem_get_name(s), LEFT);
1386 table_set_value_int(t, ++col, row, num_ns, LEFT);
1387 table_set_value_int(t, ++col, row, num_path, LEFT);
1388 table_set_value_int(t, ++col, row, num_ctrl, LEFT);
1389 table_set_value_str(t, ++col, row,
1390 iopolicy ? iopolicy : "NA", LEFT);
1391 table_set_value_str(t, ++col, row, r_iops_str, LEFT);
1392 table_set_value_str(t, ++col, row, w_iops_str, LEFT);
1393 table_set_value_str(t, ++col, row, r_clat_str, LEFT);
1394 table_set_value_str(t, ++col, row, w_clat_str, LEFT);
1395 table_set_value_str(t, ++col, row, r_bw_str, LEFT);
1396 table_set_value_str(t, ++col, row, w_bw_str, LEFT);
1397 table_set_value_double(t, ++col, row, max_util, LEFT);
1398
1399 table_add_row(t, row);
1400 }
1401
1402 table_print_stream(stream, t);
1403
1404 fprintf(stream, "\n--------------------------------------\n");
1405 fprintf(stream, "[up/down arrow keys to navigate, Enter to view, q to quit]\n");
1406
1407 /*
1408 * Footer rows are calculated manually.
1409 * The first row is empty (adds spaces) followed by another row for
1410 * dashes and the last row adds footer string.
1411 */
1412 dashboard_set_footer_rows(db_ctx, 3);
1413
1414 /* highlight the last footer row */
1415 dashboard_set_footer_row_reverse(db_ctx, 2);
1416
1417free_tbl:
1418 table_free(t);
1419 return ret;
1420}
1421
1422void stdout_top(int refresh_interval)
1423{
1424 FILE *stream;
1425 enum event_type event;
1426 struct dashboard_ctx *db_ctx;
1427 libnvme_host_t h;
1428 libnvme_subsystem_t s;
1429 __cleanup_nvme_global_ctx__attribute__((cleanup(cleanup_nvme_global_ctx))) struct libnvme_global_ctx *ctx = NULL((void*)0);
1430 __cleanup_free__attribute__((cleanup(freep))) libnvme_subsystem_t *subsys_arr = NULL((void*)0);
1431 int data_start, frame_rows, quit = 0, scroll = 0;
1432 int num_subsys = 0, subsys_idx = 0;
1433
1434 ctx = stdout_top_rescan_topology();
1435 if (!ctx
0.1
'ctx' is non-null
)
1
Taking false branch
1436 return;
1437 subsys_arr = stdout_top_build_subsys_arr(ctx, &num_subsys);
1438 if (!subsys_arr
1.1
'subsys_arr' is non-null
)
2
Taking false branch
1439 return;
1440
1441 stream = dashboard_init(&db_ctx, refresh_interval);
1442 if (!stream)
3
Assuming 'stream' is non-null
4
Taking false branch
1443 return;
1444
1445 libnvme_for_each_host(ctx, h)for (h = libnvme_first_host(ctx); h != ((void*)0); h = libnvme_next_host
(ctx, h))
{
5
Assuming 'h' is equal to null
6
Loop condition is false. Execution continues on line 1453
1446 libnvme_for_each_subsystem(h, s)for (s = libnvme_first_subsystem(h); s != ((void*)0); s = libnvme_next_subsystem
(h, s))
1447 stdout_top_reset_stat(s);
1448 }
1449
1450 /*
1451 * We start with first subsystem highlited, so set subsystem index to 0.
1452 */
1453 subsys_idx = 0;
1454 while (!quit) {
7
Loop condition is true. Entering loop body
1455 if (stdout_top_draw_subsys_screen(db_ctx, stream, subsys_arr,
8
Assuming the condition is false
9
Taking false branch
1456 num_subsys) < 0)
1457 break;
1458draw:
1459 /* highlight the selected @subsys_idx row */
1460 dashboard_set_data_row_reverse(db_ctx, subsys_idx);
1461 if (dashboard_draw_frame(db_ctx, scroll) < 0)
10
Assuming the condition is false
11
Taking false branch
1462 break;
1463wait_for_event:
1464 event = dashboard_wait_for_event(db_ctx);
1465 switch (event) {
12
Control jumps to 'case EVENT_TYPE_NVME_UEVENT:' at line 1480
1466 case EVENT_TYPE_KEY_QUIT:
1467 case EVENT_TYPE_ERROR:
1468 quit = 1;
1469 break;
1470 case EVENT_TYPE_KEY_RETURN:
1471 dashboard_reset(db_ctx);
1472
1473 s = subsys_arr[subsys_idx];
1474 quit = stdout_top_draw_subsys_topology_screen(db_ctx,
1475 stream, s);
1476 scroll = 0;
1477 if (quit)
1478 break;
1479 fallthrough__attribute__((fallthrough));
1480 case EVENT_TYPE_NVME_UEVENT: {
1481 __cleanup_free__attribute__((cleanup(freep))) char *subsys_name = NULL((void*)0);
1482 libnvme_subsystem_t s;
1483
1484 s = subsys_arr[subsys_idx];
1485 subsys_name = strdup(libnvme_subsystem_get_name(s));
13
Memory is allocated
1486 if (!subsys_name) {
14
Assuming 'subsys_name' is non-null
15
Taking false branch
1487 quit = 1;
1488 break;
1489 }
1490 free(subsys_arr);
1491 subsys_arr = NULL((void*)0);
1492 libnvme_free_global_ctx(ctx);
1493
1494 ctx = stdout_top_rescan_topology();
1495 if (!ctx
15.1
'ctx' is null
) {
16
Taking true branch
1496 quit = 1;
17
Potential leak of memory pointed to by 'subsys_name'
1497 break;
1498 }
1499
1500 subsys_arr = stdout_top_build_subsys_arr(ctx,
1501 &num_subsys);
1502 if (!subsys_arr) {
1503 quit = 1;
1504 break;
1505 }
1506
1507 subsys_idx = stdout_top_find_subsys_by_name(subsys_arr,
1508 num_subsys, subsys_name);
1509 if (subsys_idx < 0)
1510 subsys_idx = 0;
1511
1512 break;
1513 }
1514 case EVENT_TYPE_KEY_DOWN:
1515 /*
1516 * The @num_subsys should be equal to @data_rows, so we
1517 * evaluate here that we don't move pass the last data
1518 * row (or the last subsys) if we were to shift (focus)
1519 * one row down. In case it's not possible to shift
1520 * because we are already down to the last row then
1521 * ignore key press.
1522 */
1523 if (subsys_idx + 1 < num_subsys) {
1524 subsys_idx++; /* we'll highlight this row */
1525
1526 data_start = dashboard_get_data_start(db_ctx);
1527 frame_rows = dashboard_get_frame_data_rows(
1528 db_ctx);
1529 /*
1530 * If moving to next row requires shifting the
1531 * window frame buffer by one position down then
1532 * do so.
1533 */
1534 if (subsys_idx >= data_start + frame_rows) {
1535 dashboard_set_data_start(db_ctx,
1536 data_start + 1);
1537 }
1538 /*
1539 * As we are scrolling one row down, we need to
1540 * re-draw the frame.
1541 */
1542 scroll = 1;
1543 goto draw;
1544 }
1545 goto wait_for_event;
1546 case EVENT_TYPE_KEY_UP:
1547 /*
1548 * If it's possible to move one row above the current
1549 * subsys (higlighted) row then decrease the subsys_idx
1550 * by one.
1551 */
1552 if (subsys_idx - 1 >= 0) {
1553 subsys_idx--;
1554 /*
1555 * If moving one row up requires us to shift
1556 * the window frame buffer by one position up
1557 * then do so.
1558 */
1559 data_start = dashboard_get_data_start(db_ctx);
1560 if (subsys_idx < data_start) {
1561 dashboard_set_data_start(db_ctx,
1562 data_start - 1);
1563 }
1564 /*
1565 * As we are scrolling one row up, we need to
1566 * re-draw the frame.
1567 */
1568 scroll = 1;
1569 goto draw;
1570 }
1571 goto wait_for_event;
1572 case EVENT_TYPE_TIMEOUT:
1573 /* subsys screen timed out */
1574 scroll = 0;
1575 break;
1576 case EVENT_TYPE_SIGWINCH:
1577 /*
1578 * Window size would have changed so re-draw the subsys
1579 * selection screen.
1580 */
1581 scroll = 0;
1582 break;
1583 default: /* unknown event, ignore */
1584 continue;
1585 }
1586 }
1587
1588 dashboard_exit(db_ctx);
1589}