Bug Summary

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

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