Bug Summary

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

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple x86_64-redhat-linux-gnu -O3 -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 static -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/bin/../lib/clang/22 -include /__w/nvme-cli/nvme-cli/.build-ci/nvme-config.h -I nvme.p -I . -I .. -I src -I ../src -I ccan -I ../ccan -I libnvme/src -I ../libnvme/src -I shared -I ../shared -I /usr/include/json-c -D _FILE_OFFSET_BITS=64 -D _GNU_SOURCE -U NDEBUG -internal-isystem /usr/bin/../lib/clang/22/include -internal-isystem /usr/local/include -internal-isystem /usr/bin/../lib/gcc/x86_64-redhat-linux/16/../../../../x86_64-redhat-linux/include -internal-externc-isystem /include -internal-externc-isystem /usr/include -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 -fdwarf2-cfi-asm -o /__w/nvme-cli/nvme-cli/.build-ci/scan-results/2026-08-08-045408-589-1 -x c ../src/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 "dashboard.h"
37#include "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 shr_table *t;
410 struct shr_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 = shr_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 (shr_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 const char *ana_state;
434 long command_retry_count;
435 long multipath_failover_count;
436 long command_error_count;
437
438 row = shr_table_get_row_id(t);
439 if (row < 0) {
440 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"
)
;
441 goto free_tbl;
442 }
443
444 libnvme_path_get_ana_state(p, &ana_state, "");
445 libnvme_path_get_command_retry_count(p,
446 &command_retry_count, 0);
447 libnvme_path_get_multipath_failover_count(p,
448 &multipath_failover_count, 0);
449 libnvme_path_get_command_error_count(p,
450 &command_error_count, 0);
451
452 col = -1;
453
454 shr_table_set_value_str(t, ++col, row,
455 libnvme_path_get_name(p), LEFT);
456 shr_table_set_value_str(t, ++col, row,
457 ana_state, LEFT);
458 shr_table_set_value_long(t, ++col, row,
459 command_retry_count, LEFT);
460 shr_table_set_value_long(t, ++col, row,
461 multipath_failover_count, LEFT);
462 shr_table_set_value_int(t, ++col, row,
463 command_error_count, LEFT);
464
465 shr_table_add_row(t, row);
466 }
467 }
468
469 shr_table_print_stream(stream, t);
470free_tbl:
471 shr_table_free(t);
472 return ret;
473}
474
475static int stdout_top_print_ctrl_summary(FILE *stream,
476 libnvme_subsystem_t s, bool_Bool multipath)
477{
478 int ret = 0;
479 int row, col, npaths;
480 libnvme_ctrl_t c;
481 libnvme_path_t p;
482 libnvme_ns_t n;
483 double max_util, max_rlat, max_wlat;
484 double r_iops, w_iops, r_bw, w_bw;
485 char r_bw_str[16], w_bw_str[16];
486 char r_iops_str[16], w_iops_str[16];
487 char r_clat_str[16], w_clat_str[16];
488 const char *node;
489 long reset_count, reconnect_count, command_error_count;
490 struct shr_table *t;
491 bool_Bool is_fabric = false0;
492 struct shr_table_column columns[] = {
493 {"Ctrl", LEFT, AUTO_WIDTH2147483647},
494 {"Paths", LEFT, AUTO_WIDTH2147483647},
495 {"Node", LEFT, AUTO_WIDTH2147483647},
496 {"Trtype", LEFT, AUTO_WIDTH2147483647},
497 {"Address", LEFT, AUTO_WIDTH2147483647},
498 {"State", LEFT, AUTO_WIDTH2147483647},
499 {"Resets", LEFT, AUTO_WIDTH2147483647},
500 {"Reconnects", LEFT, AUTO_WIDTH2147483647},
501 {"Errors", LEFT, AUTO_WIDTH2147483647},
502 {"r_IOPS", LEFT, 9},
503 {"w_IOPS", LEFT, 9},
504 {"r_clat", LEFT, 8},
505 {"w_clat", LEFT, 8},
506 {"r_bw", LEFT, 13},
507 {"w_bw", LEFT, 13},
508 {"Util%", LEFT, 6},
509 };
510
511 t = shr_table_create();
512 if (!t) {
513 nvme_show_error("Failed to init ctrl summary table")nvme_show_message(1, "Failed to init ctrl summary table");
514 return 1;
515 }
516
517 if (shr_table_add_columns_filter(t, columns, ARRAY_SIZE(columns)(sizeof(columns) / sizeof((columns)[0])),
518 stdout_top_print_ctrl_summary_tbl_filter, (void *)s) < 0) {
519 nvme_show_error("Failed to add columns to ctrl summary table")nvme_show_message(1, "Failed to add columns to ctrl summary table"
)
;
520 ret = 1;
521 goto free_tbl;
522 }
523
524 c = libnvme_subsystem_first_ctrl(s);
525 if (c)
526 is_fabric = stdout_top_nvme_ctrl_is_fabric(c);
527
528 fprintf(stream, "\n---------- Controller Summary --------\n\n");
529 libnvme_subsystem_for_each_ctrl(s, c)for (c = libnvme_subsystem_first_ctrl(s); c != ((void*)0); c =
libnvme_subsystem_next_ctrl(s, c))
{
530 npaths = 0;
531 r_iops = w_iops = 0;
532 r_bw = w_bw = 0;
533 max_util = max_rlat = max_wlat = 0;
534
535 row = shr_table_get_row_id(t);
536 if (row < 0) {
537 nvme_show_error("Failed to add row to ctrl summary table")nvme_show_message(1, "Failed to add row to ctrl summary table"
)
;
538 ret = 1;
539 goto free_tbl;
540 }
541
542 if (multipath) {
543 libnvme_ctrl_for_each_path(c, p)for (p = libnvme_ctrl_first_path(c); p != ((void*)0); p = libnvme_ctrl_next_path
(c, p))
{
544
545 /* count num of paths per controller */
546 npaths++;
547
548 nvme_path_calc_aggr_stat(p,
549 &r_iops, &w_iops,
550 &r_bw, &w_bw,
551 &max_rlat, &max_wlat,
552 &max_util);
553 }
554 } else {
555 libnvme_ctrl_for_each_ns(c, n)for (n = libnvme_ctrl_first_ns(c); n != ((void*)0); n = libnvme_ctrl_next_ns
(c, n))
{
556 nvme_ns_calc_aggr_stat(n,
557 &r_iops, &w_iops,
558 &r_bw, &w_bw,
559 &max_rlat, &max_wlat,
560 &max_util);
561 }
562 }
563
564 nvme_format_iops(r_iops, r_iops_str, sizeof(r_iops_str));
565 nvme_format_iops(w_iops, w_iops_str, sizeof(w_iops_str));
566
567 nvme_format_bw(r_bw, r_bw_str, sizeof(r_bw_str));
568 nvme_format_bw(w_bw, w_bw_str, sizeof(w_bw_str));
569
570 nvme_format_lat(max_rlat, r_clat_str, sizeof(r_clat_str));
571 nvme_format_lat(max_wlat, w_clat_str, sizeof(w_clat_str));
572
573 libnvme_ctrl_get_numa_node(c, &node, "-1");
574 if (!strcmp(node, "-1"))
575 node = "NUMA_NO_NODE";
576
577 col = -1;
578
579 shr_table_set_value_str(t, ++col, row,
580 libnvme_ctrl_get_name(c), LEFT);
581 if (multipath)
582 shr_table_set_value_int(t, ++col, row, npaths, LEFT);
583
584 shr_table_set_value_str(t, ++col, row, node, LEFT);
585 shr_table_set_value_str(t, ++col, row,
586 libnvme_ctrl_get_transport(c), LEFT);
587 shr_table_set_value_str(t, ++col, row,
588 libnvme_ctrl_get_traddr(c), LEFT);
589 shr_table_set_value_str(t, ++col, row,
590 libnvme_ctrl_get_state(c), LEFT);
591
592 libnvme_ctrl_get_reset_count(c, &reset_count, 0);
593 shr_table_set_value_long(t, ++col, row, reset_count, LEFT);
594
595 if (is_fabric) {
596 libnvme_ctrl_get_reconnect_count(c, &reconnect_count,
597 0);
598 shr_table_set_value_long(t, ++col, row,
599 reconnect_count, LEFT);
600 }
601
602 libnvme_ctrl_get_command_error_count(c, &command_error_count,
603 0);
604 shr_table_set_value_long(t, ++col, row, command_error_count, LEFT);
605 shr_table_set_value_str(t, ++col, row, r_iops_str, LEFT);
606 shr_table_set_value_str(t, ++col, row, w_iops_str, LEFT);
607 shr_table_set_value_str(t, ++col, row, r_clat_str, LEFT);
608 shr_table_set_value_str(t, ++col, row, w_clat_str, LEFT);
609 shr_table_set_value_str(t, ++col, row, r_bw_str, LEFT);
610 shr_table_set_value_str(t, ++col, row, w_bw_str, LEFT);
611 shr_table_set_value_double(t, ++col, row, max_util, LEFT);
612
613 shr_table_add_row(t, row);
614 }
615
616 shr_table_print_stream(stream, t);
617free_tbl:
618 shr_table_free(t);
619 return ret;
620}
621
622static int stdout_top_print_ns_stat(FILE *stream, libnvme_subsystem_t s)
623{
624 int ret = 0;
625 libnvme_ns_t n;
626 libnvme_ctrl_t c;
627 int col, row;
628 unsigned int inflights;
629 double r_iops, w_iops, r_lat, w_lat, r_bw, w_bw, util;
630 char r_bw_str[16], w_bw_str[16];
631 char r_iops_str[16], w_iops_str[16];
632 char r_clat_str[16], w_clat_str[16];
633 struct shr_table *t;
634 struct shr_table_column columns[] = {
635 {"Namespace", LEFT, AUTO_WIDTH2147483647},
636 {"NSID", LEFT, AUTO_WIDTH2147483647},
637 {"Ctrl", LEFT, AUTO_WIDTH2147483647},
638 {"Retries", LEFT, AUTO_WIDTH2147483647},
639 {"Errors", LEFT, AUTO_WIDTH2147483647},
640 {"r_IOPS", LEFT, 9},
641 {"w_IOPS", LEFT, 9},
642 {"r_clat", LEFT, 8},
643 {"w_clat", LEFT, 8},
644 {"r_bw", LEFT, 13},
645 {"w_bw", LEFT, 13},
646 {"Inflights", LEFT, AUTO_WIDTH2147483647},
647 {"Util%", LEFT, 6},
648 };
649
650 t = shr_table_create();
651 if (!t) {
652 nvme_show_error("Failed to init ns stat table\n")nvme_show_message(1, "Failed to init ns stat table\n");
653 return 1;
654 }
655
656 if (shr_table_add_columns(t, columns, ARRAY_SIZE(columns)(sizeof(columns) / sizeof((columns)[0]))) < 0) {
657 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"
)
;
658 ret = 1;
659 goto free_tbl;
660 }
661
662 fprintf(stream, "----------- Namespace Stat -----------\n\n");
663 libnvme_subsystem_for_each_ctrl(s, c)for (c = libnvme_subsystem_first_ctrl(s); c != ((void*)0); c =
libnvme_subsystem_next_ctrl(s, c))
{
664 libnvme_ctrl_for_each_ns(c, n)for (n = libnvme_ctrl_first_ns(c); n != ((void*)0); n = libnvme_ctrl_next_ns
(c, n))
{
665 long command_retry_count, command_error_count;
666
667 r_iops = r_lat = r_bw = 0;
668 w_iops = w_lat = w_bw = 0;
669 util = inflights = 0;
670
671 nvme_ns_calc_stat(n,
672 &r_iops, &w_iops,
673 &r_lat, &w_lat,
674 &r_bw, &w_bw,
675 &util, &inflights);
676
677 nvme_format_iops(r_iops, r_iops_str,
678 sizeof(r_iops_str));
679 nvme_format_iops(w_iops, w_iops_str,
680 sizeof(w_iops_str));
681
682 nvme_format_bw(r_bw, r_bw_str, sizeof(r_bw_str));
683 nvme_format_bw(w_bw, w_bw_str, sizeof(w_bw_str));
684
685 nvme_format_lat(r_lat, r_clat_str, sizeof(r_clat_str));
686 nvme_format_lat(w_lat, w_clat_str, sizeof(w_clat_str));
687
688 row = shr_table_get_row_id(t);
689 if (row < 0) {
690 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");
691 ret = 1;
692 goto free_tbl;
693 }
694
695 col = -1;
696
697 shr_table_set_value_str(t, ++col, row,
698 libnvme_ns_get_name(n), LEFT);
699 shr_table_set_value_int(t, ++col, row,
700 libnvme_ns_get_nsid(n), LEFT);
701 shr_table_set_value_str(t, ++col, row,
702 libnvme_ctrl_get_name(c), LEFT);
703 libnvme_ns_get_command_retry_count(n,
704 &command_retry_count, 0);
705 libnvme_ns_get_command_error_count(n,
706 &command_error_count, 0);
707 shr_table_set_value_long(t, ++col, row,
708 command_retry_count, LEFT);
709 shr_table_set_value_long(t, ++col, row,
710 command_error_count, LEFT);
711 shr_table_set_value_str(t, ++col, row, r_iops_str, LEFT);
712 shr_table_set_value_str(t, ++col, row, w_iops_str, LEFT);
713 shr_table_set_value_str(t, ++col, row, r_clat_str, LEFT);
714 shr_table_set_value_str(t, ++col, row, w_clat_str, LEFT);
715 shr_table_set_value_str(t, ++col, row, r_bw_str, LEFT);
716 shr_table_set_value_str(t, ++col, row, w_bw_str, LEFT);
717 shr_table_set_value_unsigned(t, ++col, row, inflights,
718 LEFT);
719 shr_table_set_value_double(t, ++col, row, util, LEFT);
720
721 shr_table_add_row(t, row);
722 }
723 }
724
725 shr_table_print_stream(stream, t);
726free_tbl:
727 shr_table_free(t);
728 return ret;
729}
730
731static int stdout_top_print_nshead_stat(FILE *stream, libnvme_subsystem_t s)
732{
733 int ret = 0;
734 libnvme_ns_t n;
735 libnvme_path_t p;
736 double r_iops, w_iops, r_lat, w_lat, r_bw, w_bw, util;
737 unsigned int inflights;
738 int col, row, npaths;
739 char r_iops_str[16], w_iops_str[16];
740 char r_clat_str[16], w_clat_str[16];
741 char r_bw_str[16], w_bw_str[16];
742 struct shr_table *t;
743 struct shr_table_column columns[] = {
744 {"NSHead", LEFT, AUTO_WIDTH2147483647},
745 {"NSID", LEFT, AUTO_WIDTH2147483647},
746 {"Paths", LEFT, AUTO_WIDTH2147483647},
747 {"Requeue-IO", LEFT, AUTO_WIDTH2147483647},
748 {"Fail-IO", LEFT, AUTO_WIDTH2147483647},
749 {"r_IOPS", LEFT, 9},
750 {"w_IOPS", LEFT, 9},
751 {"r_clat", LEFT, 8},
752 {"w_clat", LEFT, 8},
753 {"r_bw", LEFT, 13},
754 {"w_bw", LEFT, 13},
755 {"Inflights", LEFT, AUTO_WIDTH2147483647},
756 {"Util%", LEFT, 6},
757 };
758
759 t = shr_table_create();
760 if (!t) {
761 nvme_show_error("Failed to init nshead stat table\n")nvme_show_message(1, "Failed to init nshead stat table\n");
762 return 1;
763 }
764
765 if (shr_table_add_columns(t, columns, ARRAY_SIZE(columns)(sizeof(columns) / sizeof((columns)[0]))) < 0) {
766 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"
)
;
767 ret = 1;
768 goto free_tbl;
769 }
770
771 fprintf(stream, "------------ NSHead Stat -------------\n\n");
772 libnvme_subsystem_for_each_ns(s, n)for (n = libnvme_subsystem_first_ns(s); n != ((void*)0); n = libnvme_subsystem_next_ns
(s, n))
{
773 long io_requeue_no_usable_path_count;
774 long io_fail_no_available_path_count;
775
776 npaths = 0;
777 r_iops = r_lat = r_bw = 0;
778 w_iops = w_lat = w_bw = 0;
779 util = inflights = 0;
780
781 nvme_ns_calc_stat(n,
782 &r_iops, &w_iops,
783 &r_lat, &w_lat,
784 &r_bw, &w_bw,
785 &util, &inflights);
786
787 nvme_format_iops(r_iops, r_iops_str, sizeof(r_iops_str));
788 nvme_format_iops(w_iops, w_iops_str, sizeof(w_iops_str));
789
790 nvme_format_bw(r_bw, r_bw_str, sizeof(r_bw_str));
791 nvme_format_bw(w_bw, w_bw_str, sizeof(w_bw_str));
792
793 nvme_format_lat(r_lat, r_clat_str, sizeof(r_clat_str));
794 nvme_format_lat(w_lat, w_clat_str, sizeof(w_clat_str));
795
796 libnvme_namespace_for_each_path(n, p)for (p = libnvme_namespace_first_path(n); p != ((void*)0); p =
libnvme_namespace_next_path(n, p))
797 npaths++;
798
799 row = shr_table_get_row_id(t);
800 if (row < 0) {
801 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"
)
;
802 ret = 1;
803 goto free_tbl;
804 }
805
806 col = -1;
807
808 shr_table_set_value_str(t, ++col, row, libnvme_ns_get_name(n),
809 LEFT);
810 shr_table_set_value_int(t, ++col, row, libnvme_ns_get_nsid(n),
811 LEFT);
812 shr_table_set_value_int(t, ++col, row, npaths, LEFT);
813 libnvme_ns_get_io_requeue_no_usable_path_count(n,
814 &io_requeue_no_usable_path_count, 0);
815 libnvme_ns_get_io_fail_no_available_path_count(n,
816 &io_fail_no_available_path_count, 0);
817 shr_table_set_value_long(t, ++col, row,
818 io_requeue_no_usable_path_count, LEFT);
819 shr_table_set_value_long(t, ++col, row,
820 io_fail_no_available_path_count, LEFT);
821 shr_table_set_value_str(t, ++col, row, r_iops_str, LEFT);
822 shr_table_set_value_str(t, ++col, row, w_iops_str, LEFT);
823 shr_table_set_value_str(t, ++col, row, r_clat_str, LEFT);
824 shr_table_set_value_str(t, ++col, row, w_clat_str, LEFT);
825 shr_table_set_value_str(t, ++col, row, r_bw_str, LEFT);
826 shr_table_set_value_str(t, ++col, row, w_bw_str, LEFT);
827 shr_table_set_value_unsigned(t, ++col, row, inflights, LEFT);
828 shr_table_set_value_double(t, ++col, row, util, LEFT);
829
830 shr_table_add_row(t, row);
831 }
832
833 shr_table_print_stream(stream, t);
834free_tbl:
835 shr_table_free(t);
836 return ret;
837}
838
839static int stdout_top_print_path_perf(FILE *stream, libnvme_subsystem_t s)
840{
841 int ret = 0;
842 libnvme_ns_t n;
843 libnvme_path_t p;
844 libnvme_ctrl_t c;
845 unsigned int inflights;
846 int row, col;
847 double util, r_iops, w_iops, r_lat, w_lat, r_bw, w_bw;
848 char r_iops_str[16], w_iops_str[16];
849 char r_clat_str[16], w_clat_str[16];
850 char r_bw_str[16], w_bw_str[16];
851 bool_Bool first;
852 struct shr_table *t;
853 const char *iopolicy;
854 struct shr_table_column columns[] = {
855 {"NSHead", LEFT, AUTO_WIDTH2147483647},
856 {"NSID", LEFT, AUTO_WIDTH2147483647},
857 {"NSPath", LEFT, AUTO_WIDTH2147483647},
858 {"Nodes", LEFT, AUTO_WIDTH2147483647},
859 {"Qdepth", LEFT, AUTO_WIDTH2147483647},
860 {"Ctrl", LEFT, AUTO_WIDTH2147483647},
861 {"r_IOPS", LEFT, 9},
862 {"w_IOPS", LEFT, 9},
863 {"r_clat", LEFT, 8},
864 {"w_clat", LEFT, 8},
865 {"r_bw", LEFT, 13},
866 {"w_bw", LEFT, 13},
867 {"Inflights", LEFT, AUTO_WIDTH2147483647},
868 {"Util%", LEFT, 6},
869 };
870
871 t = shr_table_create();
872 if (!t) {
873 nvme_show_error("Failed to init path perf table")nvme_show_message(1, "Failed to init path perf table");
874 return 1;
875 }
876
877 if (shr_table_add_columns_filter(t, columns, ARRAY_SIZE(columns)(sizeof(columns) / sizeof((columns)[0])),
878 subsystem_iopolicy_filter, (void *)s) < 0) {
879 nvme_show_error("Failed to add columns to path perf table")nvme_show_message(1, "Failed to add columns to path perf table"
)
;
880 ret = 1;
881 goto free_tbl;
882 }
883
884 libnvme_subsystem_get_iopolicy(s, &iopolicy, "");
885
886 fprintf(stream, "\n---------- Path Performance ----------\n\n");
887 libnvme_subsystem_for_each_ns(s, n)for (n = libnvme_subsystem_first_ns(s); n != ((void*)0); n = libnvme_subsystem_next_ns
(s, n))
{
888 first = true1;
889 libnvme_namespace_for_each_path(n, p)for (p = libnvme_namespace_first_path(n); p != ((void*)0); p =
libnvme_namespace_next_path(n, p))
{
890 r_iops = r_lat = r_bw = 0;
891 w_iops = w_lat = w_bw = 0;
892 util = inflights = 0;
893
894 nvme_path_calc_stat(p,
895 &r_iops, &w_iops,
896 &r_lat, &w_lat,
897 &r_bw, &w_bw,
898 &util, &inflights);
899
900 nvme_format_iops(r_iops, r_iops_str,
901 sizeof(r_iops_str));
902 nvme_format_iops(w_iops, w_iops_str,
903 sizeof(w_iops_str));
904
905 nvme_format_bw(r_bw, r_bw_str, sizeof(r_bw_str));
906 nvme_format_bw(w_bw, w_bw_str, sizeof(w_bw_str));
907
908 nvme_format_lat(r_lat, r_clat_str, sizeof(r_clat_str));
909 nvme_format_lat(w_lat, w_clat_str, sizeof(w_clat_str));
910
911 /* get controller associated with the path */
912 c = libnvme_path_get_ctrl(p);
913
914 row = shr_table_get_row_id(t);
915 if (row < 0) {
916 nvme_show_error("Failed to add row to path perf table")nvme_show_message(1, "Failed to add row to path perf table");
917 ret = 1;
918 goto free_tbl;
919 }
920
921 /*
922 * For the first row we print actual NSHead name,
923 * however, for the subsequent rows we print "arrow"
924 * ("-->") symbol for NSHead. This "arrow" style makes
925 * it visually obvious that subsequent entries (if
926 * present) are a path under the first NSHead.
927 */
928 col = -1;
929
930 if (first) {
931 shr_table_set_value_str(t, ++col, row,
932 libnvme_ns_get_name(n), LEFT);
933 first = false0;
934 } else
935 shr_table_set_value_str(t, ++col, row,
936 "-->", CENTERED);
937
938 shr_table_set_value_int(t, ++col, row,
939 libnvme_ns_get_nsid(n), CENTERED);
940 shr_table_set_value_str(t, ++col, row,
941 libnvme_path_get_name(p), LEFT);
942
943 if (!strcmp(iopolicy, "numa")) {
944 const char *numa_nodes;
945
946 libnvme_path_get_numa_nodes(p, &numa_nodes, "");
947 shr_table_set_value_str(t, ++col, row,
948 numa_nodes, CENTERED);
949 } else if (!strcmp(iopolicy, "queue-depth")) {
950 int queue_depth;
951
952 libnvme_path_get_queue_depth(p, &queue_depth,
953 0);
954 shr_table_set_value_int(t, ++col, row,
955 queue_depth, CENTERED);
956 }
957
958 shr_table_set_value_str(t, ++col, row,
959 libnvme_ctrl_get_name(c), LEFT);
960 shr_table_set_value_str(t, ++col, row, r_iops_str, LEFT);
961 shr_table_set_value_str(t, ++col, row, w_iops_str, LEFT);
962 shr_table_set_value_str(t, ++col, row, r_clat_str, LEFT);
963 shr_table_set_value_str(t, ++col, row, w_clat_str, LEFT);
964 shr_table_set_value_str(t, ++col, row, r_bw_str, LEFT);
965 shr_table_set_value_str(t, ++col, row, w_bw_str, LEFT);
966 shr_table_set_value_unsigned(t, ++col, row,
967 inflights, LEFT);
968 shr_table_set_value_double(t, ++col, row, util, LEFT);
969
970 shr_table_add_row(t, row);
971 }
972 }
973 shr_table_print_stream(stream, t);
974free_tbl:
975 shr_table_free(t);
976 return ret;
977}
978
979static void stdout_top_print_subsys_topology_config(FILE *stream,
980 libnvme_subsystem_t s)
981{
982 int len = strlen(libnvme_subsystem_get_name(s));
983 const char *iopolicy;
984 const char *model;
985 const char *serial;
986 const char *firmware;
987
988 libnvme_subsystem_get_iopolicy(s, &iopolicy, "");
989 libnvme_subsystem_get_model(s, &model, "undefined");
990 libnvme_subsystem_get_serial(s, &serial, "");
991 libnvme_subsystem_get_firmware(s, &firmware, "");
992
993 fprintf(stream, "%s - NQN=%s\n", libnvme_subsystem_get_name(s),
994 libnvme_subsystem_get_subsysnqn(s));
995 fprintf(stream, "%*s hostnqn=%s\n", len, " ",
996 libnvme_host_get_hostnqn(libnvme_subsystem_get_host(s)));
997 fprintf(stream, "%*s iopolicy=%s\n", len, " ", iopolicy);
998
999 fprintf(stream, "%*s model=%s\n", len, " ", model);
1000 fprintf(stream, "%*s serial=%s\n", len, " ", serial);
1001 fprintf(stream, "%*s firmware=%s\n", len, " ", firmware);
1002 fprintf(stream, "%*s type=%s\n", len, " ",
1003 libnvme_subsystem_get_subsystype(s));
1004
1005 fprintf(stream, "\n");
1006}
1007
1008static int stdout_top_update_stat(libnvme_subsystem_t s)
1009{
1010 libnvme_ctrl_t c;
1011 libnvme_ns_t n;
1012 libnvme_path_t p;
1013 int ret;
1014
1015 if (nvme_is_multipath(s)) {
1016 libnvme_subsystem_for_each_ns(s, n)for (n = libnvme_subsystem_first_ns(s); n != ((void*)0); n = libnvme_subsystem_next_ns
(s, n))
{
1017 ret = libnvme_ns_update_stat(n, true1);
1018 if (ret < 0) {
1019 nvme_show_error("Failed to update namespace stat")nvme_show_message(1, "Failed to update namespace stat");
1020 return ret;
1021 }
1022
1023 libnvme_namespace_for_each_path(n, p)for (p = libnvme_namespace_first_path(n); p != ((void*)0); p =
libnvme_namespace_next_path(n, p))
{
1024 ret = libnvme_path_update_stat(p, true1);
1025 if (ret < 0) {
1026 nvme_show_error("Failed to update path stat")nvme_show_message(1, "Failed to update path stat");
1027 return ret;
1028 }
1029 }
1030 }
1031 } else {
1032 libnvme_subsystem_for_each_ctrl(s, c)for (c = libnvme_subsystem_first_ctrl(s); c != ((void*)0); c =
libnvme_subsystem_next_ctrl(s, c))
{
1033 libnvme_ctrl_for_each_ns(c, n)for (n = libnvme_ctrl_first_ns(c); n != ((void*)0); n = libnvme_ctrl_next_ns
(c, n))
{
1034 ret = libnvme_ns_update_stat(n, true1);
1035 if (ret < 0) {
1036 nvme_show_error("Failed to update namespace stat")nvme_show_message(1, "Failed to update namespace stat");
1037 return ret;
1038 }
1039 }
1040 }
1041 }
1042
1043 return 0;
1044}
1045
1046static void stdout_top_reset_stat(libnvme_subsystem_t s)
1047{
1048 libnvme_ctrl_t c;
1049 libnvme_ns_t n;
1050 libnvme_path_t p;
1051
1052 if (nvme_is_multipath(s)) {
1053 libnvme_subsystem_for_each_ns(s, n)for (n = libnvme_subsystem_first_ns(s); n != ((void*)0); n = libnvme_subsystem_next_ns
(s, n))
{
1054 libnvme_ns_reset_stat(n);
1055
1056 libnvme_namespace_for_each_path(n, p)for (p = libnvme_namespace_first_path(n); p != ((void*)0); p =
libnvme_namespace_next_path(n, p))
1057 libnvme_path_reset_stat(p);
1058 }
1059 } else {
1060 libnvme_subsystem_for_each_ctrl(s, c)for (c = libnvme_subsystem_first_ctrl(s); c != ((void*)0); c =
libnvme_subsystem_next_ctrl(s, c))
{
1061
1062 libnvme_ctrl_for_each_ns(c, n)for (n = libnvme_ctrl_first_ns(c); n != ((void*)0); n = libnvme_ctrl_next_ns
(c, n))
1063 libnvme_ns_reset_stat(n);
1064 }
1065 }
1066}
1067
1068static int stdout_top_print_subsys_topology(struct dashboard_ctx *db_ctx,
1069 FILE *stream, libnvme_subsystem_t s)
1070{
1071 int ret = 0;
1072 bool_Bool multipath = nvme_is_multipath(s);
1073
1074 ret = stdout_top_update_stat(s);
1075 if (ret)
1076 return ret;
1077
1078 stdout_top_print_subsys_topology_config(stream, s);
1079
1080 if (multipath) {
1081 ret = stdout_top_print_nshead_stat(stream, s);
1082 if (ret)
1083 return ret;
1084
1085 ret = stdout_top_print_path_perf(stream, s);
1086 if (ret)
1087 return ret;
1088
1089 ret = stdout_top_print_path_health(stream, s);
1090 if (ret)
1091 return ret;
1092 } else {
1093 ret = stdout_top_print_ns_stat(stream, s);
1094 if (ret)
1095 return ret;
1096 }
1097
1098 ret = stdout_top_print_ctrl_summary(stream, s, multipath);
1099
1100 return ret;
1101}
1102
1103static void stdout_top_print_subsys_topology_header(
1104 struct dashboard_ctx *db_ctx, FILE *stream)
1105{
1106 fprintf(stream, "---- nvme-top - Refresh: %d Second ----\n",
1107 dashboard_get_interval(db_ctx));
1108
1109 dashboard_set_header_rows(db_ctx, 1);
1110
1111 /* highlight the header row */
1112 dashboard_set_header_row_reverse(db_ctx, 0);
1113}
1114
1115static void stdout_top_print_subsys_topology_footer(
1116 struct dashboard_ctx *db_ctx, FILE *stream)
1117{
1118 fprintf(stream, "\n--------------------------------------\n");
1119 fprintf(stream, "[up/down keys to navigate, ESC to go back to the previous screen, q to quit]\n");
1120
1121 dashboard_set_footer_rows(db_ctx, 3);
1122
1123 /* hightligh the last footer row */
1124 dashboard_set_footer_row_reverse(db_ctx, 2);
1125}
1126
1127static libnvme_subsystem_t stdout_top_search_subsystem(
1128 struct libnvme_global_ctx *ctx, const char *subsys_name)
1129{
1130 libnvme_host_t h;
1131 libnvme_subsystem_t s;
1132
1133 libnvme_for_each_host(ctx, h)for (h = libnvme_first_host(ctx); h != ((void*)0); h = libnvme_next_host
(ctx, h))
{
1134 libnvme_for_each_subsystem(h, s)for (s = libnvme_first_subsystem(h); s != ((void*)0); s = libnvme_next_subsystem
(h, s))
{
1135 if (!strcmp(libnvme_subsystem_get_name(s), subsys_name))
1136 return s;
1137 }
1138 }
1139
1140 return NULL((void*)0);
1141}
1142
1143static libnvme_subsystem_t *stdout_top_build_subsys_arr(
1144 struct libnvme_global_ctx *ctx, int *num_subsys)
1145{
1146 libnvme_host_t h;
1147 libnvme_subsystem_t s;
1148 libnvme_subsystem_t *subsys_arr;
1149 int subsys_idx = 0;
1150 int n = 0;
1151
1152 libnvme_for_each_host(ctx, h)for (h = libnvme_first_host(ctx); h != ((void*)0); h = libnvme_next_host
(ctx, h))
1153 libnvme_for_each_subsystem(h, s)for (s = libnvme_first_subsystem(h); s != ((void*)0); s = libnvme_next_subsystem
(h, s))
1154 n++;
1155 if (!n) {
1156 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"
)
;
1157 return NULL((void*)0);
1158 }
1159
1160 subsys_arr = calloc(n, sizeof(libnvme_subsystem_t));
1161 if (!subsys_arr) {
1162 nvme_show_error("Failed to allocate memory for subsys array\n")nvme_show_message(1, "Failed to allocate memory for subsys array\n"
)
;
1163 return NULL((void*)0);
1164 }
1165
1166 libnvme_for_each_host(ctx, h)for (h = libnvme_first_host(ctx); h != ((void*)0); h = libnvme_next_host
(ctx, h))
{
1167 libnvme_for_each_subsystem(h, s)for (s = libnvme_first_subsystem(h); s != ((void*)0); s = libnvme_next_subsystem
(h, s))
1168 subsys_arr[subsys_idx++] = s;
1169 }
1170
1171 *num_subsys = n;
1172 return subsys_arr;
1173}
1174
1175static int stdout_top_find_subsys_by_name(libnvme_subsystem_t *subsys_arr,
1176 int num_subsys, const char *subsys_name)
1177{
1178 int idx;
1179 libnvme_subsystem_t s;
1180
1181 for (idx = 0; idx < num_subsys; idx++) {
1182 s = subsys_arr[idx];
1183
1184 if (!strcmp(libnvme_subsystem_get_name(s), subsys_name))
1185 return idx;
1186 }
1187
1188 return -1;
1189}
1190
1191static int handle_event_page_down(struct dashboard_ctx *db_ctx)
1192{
1193 int data_start, data_rows, frame_rows;
1194 int scroll_down, scroll = 0;
1195
1196 data_start = dashboard_get_data_start(db_ctx);
1197 data_rows = dashboard_get_data_rows(db_ctx);
1198 frame_rows = dashboard_get_frame_data_rows(db_ctx);
1199
1200 /*
1201 * Scroll down max up to one page frame from current data_start index.
1202 * While scrolling down, ensure we don't move past the max available
1203 * data rows. If we are already on the last page frame or there's no
1204 * data if move past the current page frame then ignore the key press.
1205 */
1206 scroll_down = data_start + frame_rows;
1207
1208 if (scroll_down < data_rows) {
1209 dashboard_set_data_start(db_ctx, scroll_down);
1210 scroll = 1;
1211 }
1212
1213 return scroll;
1214}
1215
1216static int handle_event_page_up(struct dashboard_ctx *db_ctx)
1217{
1218 int data_start, frame_rows, off;
1219 int scroll_up, scroll = 0;
1220
1221 data_start = dashboard_get_data_start(db_ctx);
1222 frame_rows = dashboard_get_frame_data_rows(db_ctx);
1223
1224 /*
1225 * Scroll back up max up to one page frame. Determine the num of data
1226 * rows we can scroll back up based on current data start index and max
1227 * num of rows which could be drawn in one page frame. If we're already
1228 * on the first page frame and hence we can't scroll back then ignore
1229 * the key press.
1230 */
1231 off = min(data_start, frame_rows)((data_start) > (frame_rows) ? (frame_rows) : (data_start)
)
;
1232 scroll_up = data_start - off;
1233
1234 if (scroll_up != data_start) {
1235 dashboard_set_data_start(db_ctx, scroll_up);
1236 scroll = 1;
1237 }
1238
1239 return scroll;
1240}
1241
1242/*
1243 * Draws subsys topology screen of susbystem @s
1244 * Returns: 0 if ESC key is pressed or needs to draw subsystem selection screen
1245 * 1 if 'q' is pressed or in case of error
1246 */
1247static int stdout_top_draw_subsys_topology_screen(
1248 struct libnvme_global_ctx *ctx, struct dashboard_ctx *db_ctx,
1249 FILE *stream, libnvme_subsystem_t _s)
1250{
1251 enum event_type event;
1252 int ret, scroll = 0;
1253 int data_start, data_rows;
1254 __cleanup_free__attribute__((cleanup(shr_freep))) const char *subsys_name;
1255 libnvme_subsystem_t s = _s;
1256
1257 subsys_name = strdup(libnvme_subsystem_get_name(s));
23
Memory is allocated
1258 if (!subsys_name)
24
Assuming 'subsys_name' is non-null
25
Taking false branch
1259 return 1; /* force quit */
1260
1261 while (1) {
26
Loop condition is true. Entering loop body
1262 stdout_top_print_subsys_topology_header(db_ctx, stream);
1263 ret = stdout_top_print_subsys_topology(db_ctx, stream, s);
1264 if (ret
26.1
'ret' is 1
)
27
Taking true branch
1265 break;
28
Execution jumps to the end of the function
1266 stdout_top_print_subsys_topology_footer(db_ctx, stream);
1267
1268draw:
1269 ret = dashboard_draw_frame(db_ctx, scroll);
1270 if (ret)
1271 break;
1272wait_for_event:
1273 event = dashboard_wait_for_event(db_ctx);
1274 if (event == EVENT_TYPE_KEY_ESC) {
1275 ret = 0;
1276 dashboard_reset(db_ctx);
1277 break;
1278 } else if (event == EVENT_TYPE_KEY_UP) {
1279 data_start = dashboard_get_data_start(db_ctx);
1280 /*
1281 * If we don't move past the first data row by shifting
1282 * one data row up then do so, otherwise ignore the key
1283 * press.
1284 */
1285 if (data_start - 1 >= 0) {
1286 dashboard_set_data_start(db_ctx,
1287 data_start - 1);
1288 scroll = 1;
1289 goto draw;
1290 }
1291 goto wait_for_event;
1292 } else if (event == EVENT_TYPE_KEY_DOWN) {
1293 data_start = dashboard_get_data_start(db_ctx);
1294 data_rows = dashboard_get_data_rows(db_ctx);
1295 /*
1296 * If we don't move past the max data rows shifting one
1297 * row down then do so, otherwise ignore the key press.
1298 */
1299 if (data_start + 1 < data_rows) {
1300 dashboard_set_data_start(db_ctx,
1301 data_start + 1);
1302 scroll = 1;
1303 goto draw;
1304 }
1305 goto wait_for_event;
1306 } else if (event == EVENT_TYPE_TIMEOUT) { /* screen timed out */
1307 scroll = 0;
1308 } else if (event == EVENT_TYPE_KEY_QUIT ||
1309 event == EVENT_TYPE_ERROR) {
1310 ret = 1;
1311 break;
1312 } else if (event == EVENT_TYPE_NVME_UEVENT) {
1313
1314 if (libnvme_refresh_topology(ctx)) {
1315 ret = 1; /* force quit */
1316 break;
1317 }
1318
1319 s = stdout_top_search_subsystem(ctx, subsys_name);
1320 if (!s) {
1321 ret = 0; /* draw subsys selection screen */
1322 break;
1323 }
1324 scroll = 0;
1325 } else if (event == EVENT_TYPE_SIGWINCH) {
1326 /*
1327 * Window size would have changed so re-draw the subsys
1328 * topology screen.
1329 */
1330 scroll = 0;
1331 } else if (event == EVENT_TYPE_KEY_PAGE_DOWN) {
1332
1333 scroll = handle_event_page_down(db_ctx);
1334 if (scroll)
1335 goto draw;
1336
1337 goto wait_for_event;
1338
1339 } else if (event == EVENT_TYPE_KEY_PAGE_UP) {
1340
1341 scroll = handle_event_page_up(db_ctx);
1342 if (scroll)
1343 goto draw;
1344
1345 goto wait_for_event;
1346 } /* else ignore */
1347 }
1348
1349 return ret;
29
Potential leak of memory pointed to by 'subsys_name'
1350}
1351
1352static int stdout_top_draw_subsys_screen(struct dashboard_ctx *db_ctx,
1353 FILE *stream, libnvme_subsystem_t *subsys_arr, int num_subsys)
1354{
1355 int ret = 0;
1356 libnvme_subsystem_t s;
1357 libnvme_ctrl_t c;
1358 libnvme_ns_t n;
1359 libnvme_path_t p;
1360 int i, row, col, num_ns, num_path, num_ctrl;
1361 double r_iops, w_iops;
1362 double r_bw, w_bw;
1363 double max_rlat, max_wlat, max_util;
1364 char r_bw_str[16], w_bw_str[16];
1365 char r_iops_str[16], w_iops_str[16];
1366 char r_clat_str[16], w_clat_str[16];
1367 const char *iopolicy;
1368 struct shr_table *t;
1369 struct shr_table_column columns[] = {
1370 {"Subsystem", LEFT, AUTO_WIDTH2147483647},
1371 {"Namespaces", LEFT, AUTO_WIDTH2147483647},
1372 {"Paths", LEFT, AUTO_WIDTH2147483647},
1373 {"Ctrls", LEFT, AUTO_WIDTH2147483647},
1374 {"IOPolicy", LEFT, AUTO_WIDTH2147483647},
1375 {"r_IOPS", LEFT, 9},
1376 {"w_IOPS", LEFT, 9},
1377 {"r_clat", LEFT, 8},
1378 {"w_clat", LEFT, 8},
1379 {"r_bw", LEFT, 13},
1380 {"w_bw", LEFT, 13},
1381 {"Util%", LEFT, 6},
1382 };
1383
1384 fprintf(stream, "---- nvme-top - Refresh: %d Second ----\n",
1385 dashboard_get_interval(db_ctx));
1386 fprintf(stream, "\n--------- Subsystem Summary ----------\n\n");
1387
1388 t = shr_table_create();
1389 if (!t) {
1390 nvme_show_error("Failed to init subsys screen table\n")nvme_show_message(1, "Failed to init subsys screen table\n");
1391 return -1;
1392 }
1393
1394 if (shr_table_add_columns(t, columns, ARRAY_SIZE(columns)(sizeof(columns) / sizeof((columns)[0]))) < 0) {
1395 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"
)
;
1396 ret = -1;
1397 goto free_tbl;
1398 }
1399 /*
1400 * Header row count is calculated manually. The first row displays the
1401 * refresh interval, followed by an empty row. The third row displays
1402 * the heading followed by another empty row. The fifth row is for
1403 * displaying shr_table columns and then another row for dashes underneath
1404 * the shr_table columns.
1405 */
1406 dashboard_set_header_rows(db_ctx, 6);
1407
1408 /* highlight the first header row */
1409 dashboard_set_header_row_reverse(db_ctx, 0);
1410
1411 for (i = 0; i < num_subsys; i++) {
1412 s = subsys_arr[i];
1413 num_ctrl = num_ns = num_path = 0;
1414 r_iops = w_iops = 0;
1415 r_bw = w_bw = 0;
1416 max_rlat = max_wlat = 0;
1417 max_util = 0;
1418 libnvme_subsystem_get_iopolicy(s, &iopolicy, "NA");
1419
1420 libnvme_subsystem_for_each_ctrl(s, c)for (c = libnvme_subsystem_first_ctrl(s); c != ((void*)0); c =
libnvme_subsystem_next_ctrl(s, c))
1421 num_ctrl++;
1422
1423 ret = stdout_top_update_stat(s);
1424 if (ret)
1425 goto free_tbl;
1426
1427 if (nvme_is_multipath(s)) {
1428 libnvme_subsystem_for_each_ns(s, n)for (n = libnvme_subsystem_first_ns(s); n != ((void*)0); n = libnvme_subsystem_next_ns
(s, n))
{
1429 num_ns++;
1430
1431 libnvme_namespace_for_each_path(n, p)for (p = libnvme_namespace_first_path(n); p != ((void*)0); p =
libnvme_namespace_next_path(n, p))
1432 num_path++;
1433
1434 nvme_ns_calc_aggr_stat(n,
1435 &r_iops, &w_iops,
1436 &r_bw, &w_bw,
1437 &max_rlat, &max_wlat,
1438 &max_util);
1439 }
1440 } else {
1441 libnvme_subsystem_for_each_ctrl(s, c)for (c = libnvme_subsystem_first_ctrl(s); c != ((void*)0); c =
libnvme_subsystem_next_ctrl(s, c))
{
1442 libnvme_ctrl_for_each_ns(c, n)for (n = libnvme_ctrl_first_ns(c); n != ((void*)0); n = libnvme_ctrl_next_ns
(c, n))
{
1443 num_ns++;
1444
1445 nvme_ns_calc_aggr_stat(n,
1446 &r_iops, &w_iops,
1447 &r_bw, &w_bw,
1448 &max_rlat, &max_wlat,
1449 &max_util);
1450 }
1451 }
1452 }
1453
1454 nvme_format_iops(r_iops, r_iops_str, sizeof(r_iops_str));
1455 nvme_format_iops(w_iops, w_iops_str, sizeof(w_iops_str));
1456
1457 nvme_format_bw(r_bw, r_bw_str, sizeof(r_bw_str));
1458 nvme_format_bw(w_bw, w_bw_str, sizeof(w_bw_str));
1459
1460 nvme_format_lat(max_rlat, r_clat_str, sizeof(r_clat_str));
1461 nvme_format_lat(max_wlat, w_clat_str, sizeof(w_clat_str));
1462
1463 row = shr_table_get_row_id(t);
1464 if (row < 0) {
1465 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"
)
;
1466 ret = -1;
1467 goto free_tbl;
1468 }
1469
1470 col = -1;
1471
1472 shr_table_set_value_str(t, ++col, row,
1473 libnvme_subsystem_get_name(s), LEFT);
1474 shr_table_set_value_int(t, ++col, row, num_ns, LEFT);
1475 shr_table_set_value_int(t, ++col, row, num_path, LEFT);
1476 shr_table_set_value_int(t, ++col, row, num_ctrl, LEFT);
1477 shr_table_set_value_str(t, ++col, row, iopolicy, LEFT);
1478 shr_table_set_value_str(t, ++col, row, r_iops_str, LEFT);
1479 shr_table_set_value_str(t, ++col, row, w_iops_str, LEFT);
1480 shr_table_set_value_str(t, ++col, row, r_clat_str, LEFT);
1481 shr_table_set_value_str(t, ++col, row, w_clat_str, LEFT);
1482 shr_table_set_value_str(t, ++col, row, r_bw_str, LEFT);
1483 shr_table_set_value_str(t, ++col, row, w_bw_str, LEFT);
1484 shr_table_set_value_double(t, ++col, row, max_util, LEFT);
1485
1486 shr_table_add_row(t, row);
1487 }
1488
1489 shr_table_print_stream(stream, t);
1490
1491 fprintf(stream, "\n--------------------------------------\n");
1492 fprintf(stream, "[up/down keys to navigate, Enter to view, q to quit]\n");
1493
1494 /*
1495 * Footer rows are calculated manually.
1496 * The first row is empty (adds spaces) followed by another row for
1497 * dashes and the last row adds footer string.
1498 */
1499 dashboard_set_footer_rows(db_ctx, 3);
1500
1501 /* highlight the last footer row */
1502 dashboard_set_footer_row_reverse(db_ctx, 2);
1503
1504free_tbl:
1505 shr_table_free(t);
1506 return ret;
1507}
1508
1509void stdout_top(int refresh_interval)
1510{
1511 FILE *stream;
1512 enum event_type event;
1513 struct dashboard_ctx *db_ctx;
1514 libnvme_host_t h;
1515 libnvme_subsystem_t s;
1516 __cleanup_nvme_global_ctx__attribute__((cleanup(cleanup_nvme_global_ctx))) struct libnvme_global_ctx *ctx = NULL((void*)0);
1517 __cleanup_free__attribute__((cleanup(shr_freep))) libnvme_subsystem_t *subsys_arr = NULL((void*)0);
1518 int data_start, frame_rows, quit = 0, scroll = 0;
1519 int num_subsys = 0, subsys_idx = 0;
1520 int err;
1521
1522 err = nvme_create_global_ctx(&ctx);
1523 if (err)
1
Assuming 'err' is 0
2
Taking false branch
1524 return;
1525
1526 if (libnvme_scan_topology(ctx, NULL((void*)0), NULL((void*)0))) {
3
Assuming the condition is false
4
Taking false branch
1527 nvme_show_error("Failed to scan topology")nvme_show_message(1, "Failed to scan topology");
1528 return;
1529 }
1530
1531 subsys_arr = stdout_top_build_subsys_arr(ctx, &num_subsys);
1532 if (!subsys_arr
4.1
'subsys_arr' is non-null
)
5
Taking false branch
1533 return;
1534
1535 stream = dashboard_init(&db_ctx, refresh_interval);
1536 if (!stream)
6
Assuming 'stream' is non-null
7
Taking false branch
1537 return;
1538
1539 libnvme_for_each_host(ctx, h)for (h = libnvme_first_host(ctx); h != ((void*)0); h = libnvme_next_host
(ctx, h))
{
8
Assuming 'h' is not equal to null
9
Loop condition is true. Entering loop body
14
Assuming 'h' is equal to null
15
Loop condition is false. Execution continues on line 1547
1540 libnvme_for_each_subsystem(h, s)for (s = libnvme_first_subsystem(h); s != ((void*)0); s = libnvme_next_subsystem
(h, s))
10
Assuming 's' is not equal to null
11
Loop condition is true. Entering loop body
12
Assuming 's' is equal to null
13
Loop condition is false. Execution continues on line 1539
1541 stdout_top_reset_stat(s);
1542 }
1543
1544 /*
1545 * We start with first subsystem highlited, so set subsystem index to 0.
1546 */
1547 subsys_idx = 0;
1548 while (!quit) {
16
Loop condition is true. Entering loop body
1549 if (stdout_top_draw_subsys_screen(db_ctx, stream, subsys_arr,
17
Assuming the condition is false
18
Taking false branch
1550 num_subsys) < 0)
1551 break;
1552draw:
1553 /* highlight the selected @subsys_idx row */
1554 dashboard_set_data_row_reverse(db_ctx, subsys_idx);
1555 if (dashboard_draw_frame(db_ctx, scroll) < 0)
19
Assuming the condition is false
20
Taking false branch
1556 break;
1557wait_for_event:
1558 event = dashboard_wait_for_event(db_ctx);
1559 switch (event) {
21
Control jumps to 'case EVENT_TYPE_KEY_RETURN:' at line 1564
1560 case EVENT_TYPE_KEY_QUIT:
1561 case EVENT_TYPE_ERROR:
1562 quit = 1;
1563 break;
1564 case EVENT_TYPE_KEY_RETURN: {
1565 dashboard_reset(db_ctx);
1566 s = subsys_arr[subsys_idx];
1567
1568 quit = stdout_top_draw_subsys_topology_screen(ctx,
22
Calling 'stdout_top_draw_subsys_topology_screen'
1569 db_ctx, stream, s);
1570 if (quit)
1571 break;
1572
1573 scroll = 0;
1574 free(subsys_arr);
1575 subsys_arr = NULL((void*)0);
1576
1577 if (libnvme_refresh_topology(ctx)) {
1578 quit = 1;
1579 break;
1580 }
1581
1582 /*
1583 * The topology may have changed while the subsystem
1584 * dashboard was active. Restart from the first
1585 * subsystem instead of trying to restore the previous
1586 * screen position.
1587 */
1588 subsys_idx = 0;
1589 subsys_arr = stdout_top_build_subsys_arr(ctx,
1590 &num_subsys);
1591 if (!subsys_arr)
1592 quit = 1;
1593
1594 break;
1595 }
1596 case EVENT_TYPE_NVME_UEVENT: {
1597 __cleanup_free__attribute__((cleanup(shr_freep))) char *subsys_name = NULL((void*)0);
1598 libnvme_subsystem_t s = subsys_arr[subsys_idx];
1599
1600 subsys_name = strdup(libnvme_subsystem_get_name(s));
1601 if (!subsys_name) {
1602 quit = 1;
1603 break;
1604 }
1605
1606 free(subsys_arr);
1607 subsys_arr = NULL((void*)0);
1608
1609 if (libnvme_refresh_topology(ctx)) {
1610 quit = 1;
1611 break;
1612 }
1613
1614 subsys_arr = stdout_top_build_subsys_arr(ctx,
1615 &num_subsys);
1616 if (!subsys_arr) {
1617 quit = 1;
1618 break;
1619 }
1620
1621 subsys_idx = stdout_top_find_subsys_by_name(subsys_arr,
1622 num_subsys, subsys_name);
1623 if (subsys_idx < 0)
1624 subsys_idx = 0;
1625
1626 scroll = 0;
1627 break;
1628 }
1629 case EVENT_TYPE_KEY_DOWN:
1630 /*
1631 * The @num_subsys should be equal to @data_rows, so we
1632 * evaluate here that we don't move pass the last data
1633 * row (or the last subsys) if we were to shift (focus)
1634 * one row down. In case it's not possible to shift
1635 * because we are already down to the last row then
1636 * ignore key press.
1637 */
1638 if (subsys_idx + 1 < num_subsys) {
1639 subsys_idx++; /* we'll highlight this row */
1640
1641 data_start = dashboard_get_data_start(db_ctx);
1642 frame_rows = dashboard_get_frame_data_rows(
1643 db_ctx);
1644 /*
1645 * If moving to next row requires shifting the
1646 * window frame buffer by one position down then
1647 * do so.
1648 */
1649 if (subsys_idx >= data_start + frame_rows) {
1650 dashboard_set_data_start(db_ctx,
1651 data_start + 1);
1652 }
1653 /*
1654 * As we are scrolling one row down, we need to
1655 * re-draw the frame.
1656 */
1657 scroll = 1;
1658 goto draw;
1659 }
1660 goto wait_for_event;
1661 case EVENT_TYPE_KEY_UP:
1662 /*
1663 * If it's possible to move one row above the current
1664 * subsys (higlighted) row then decrease the subsys_idx
1665 * by one.
1666 */
1667 if (subsys_idx - 1 >= 0) {
1668 subsys_idx--;
1669 /*
1670 * If moving one row up requires us to shift
1671 * the window frame buffer by one position up
1672 * then do so.
1673 */
1674 data_start = dashboard_get_data_start(db_ctx);
1675 if (subsys_idx < data_start) {
1676 dashboard_set_data_start(db_ctx,
1677 data_start - 1);
1678 }
1679 /*
1680 * As we are scrolling one row up, we need to
1681 * re-draw the frame.
1682 */
1683 scroll = 1;
1684 goto draw;
1685 }
1686 goto wait_for_event;
1687 case EVENT_TYPE_TIMEOUT:
1688 /* subsys screen timed out */
1689 scroll = 0;
1690 break;
1691 case EVENT_TYPE_SIGWINCH:
1692 /*
1693 * Window size would have changed so re-draw the subsys
1694 * selection screen.
1695 */
1696 scroll = 0;
1697 break;
1698 case EVENT_TYPE_KEY_PAGE_DOWN:
1699 scroll = handle_event_page_down(db_ctx);
1700 if (scroll) {
1701 subsys_idx = dashboard_get_data_start(db_ctx);
1702 goto draw;
1703 }
1704
1705 goto wait_for_event;
1706 case EVENT_TYPE_KEY_PAGE_UP:
1707 scroll = handle_event_page_up(db_ctx);
1708 if (scroll) {
1709 subsys_idx = dashboard_get_data_start(db_ctx);
1710 goto draw;
1711 }
1712
1713 goto wait_for_event;
1714 default: /* unknown event, ignore */
1715 continue;
1716 }
1717 }
1718
1719 dashboard_exit(db_ctx);
1720}