Bug Summary

File:.build-ci/../src/nvme-print-stdout-top.c
Warning:line 1127, 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-11-02-081605-642-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#include <stddef.h>
19#include <stdio.h>
20
21#include <libnvme.h>
22
23#include <ccan/array_size/array_size.h>
24#include <ccan/minmax/minmax.h>
25#include <shared/table-util.h>
26
27#include "cleanup.h"
28#include "dashboard.h"
29#include "global-ctx.h"
30#include "nvme-iostat.h"
31#include "nvme-print.h"
32
33static bool_Bool stdout_top_nvme_ctrl_is_fabric(struct libnvme_ctrl *c)
34{
35 if (strcmp(libnvme_ctrl_get_transport(c), "pcie"))
36 return true1;
37 else
38 return false0;
39}
40
41static bool_Bool stdout_top_print_ctrl_summary_tbl_filter(const char *name,
42 void *arg)
43{
44 struct libnvme_ctrl *c;
45 struct libnvme_subsystem *s = arg;
46 bool_Bool multipath = nvme_is_multipath(s);
47
48 if (!strcmp(name, "Paths")) {
49 if (!multipath)
50 return false0;
51 }
52
53 if (!strcmp(name, "Reconnects")) {
54 c = libnvme_subsystem_first_ctrl(s);
55 if (c) {
56 if (stdout_top_nvme_ctrl_is_fabric(c))
57 return true1;
58 else
59 return false0;
60 }
61 }
62
63 return true1;
64}
65
66static int stdout_top_print_path_health(FILE *stream, struct libnvme_subsystem *s)
67{
68 int ret = 0;
69 int col, row;
70 struct libnvme_ns *n;
71 struct libnvme_path *p;
72 struct shr_table *t;
73 struct shr_table_column columns[] = {
74 {"NSPath", LEFT, AUTO_WIDTH2147483647},
75 {"ANAState", LEFT, AUTO_WIDTH2147483647},
76 {"Retries", LEFT, AUTO_WIDTH2147483647},
77 {"Failovers", LEFT, AUTO_WIDTH2147483647},
78 {"Errors", LEFT, AUTO_WIDTH2147483647}
79 };
80
81 t = shr_table_create();
82 if (!t) {
83 nvme_show_error("Failed to init path health table\n")nvme_show_message(1, "Failed to init path health table\n");
84 return 1;
85 }
86
87 if (shr_table_add_columns(t, columns, ARRAY_SIZE(columns)(sizeof(columns) / sizeof((columns)[0]) + 0)) < 0) {
88 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"
)
;
89 ret = 1;
90 goto free_tbl;
91 }
92
93 fprintf(stream, "\n------------ Path Health -------------\n\n");
94 libnvme_subsystem_for_each_ns(s, n)for (n = libnvme_subsystem_first_ns(s); n != ((void*)0); n = libnvme_subsystem_next_ns
(s, n))
{
95 libnvme_namespace_for_each_path(n, p)for (p = libnvme_namespace_first_path(n); p != ((void*)0); p =
libnvme_namespace_next_path(n, p))
{
96 const char *ana_state;
97 long command_retry_count;
98 long multipath_failover_count;
99 long command_error_count;
100
101 row = shr_table_get_row_id(t);
102 if (row < 0) {
103 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"
)
;
104 goto free_tbl;
105 }
106
107 libnvme_path_get_ana_state(p, &ana_state, "");
108 libnvme_path_get_command_retry_count(p,
109 &command_retry_count, 0);
110 libnvme_path_get_multipath_failover_count(p,
111 &multipath_failover_count, 0);
112 libnvme_path_get_command_error_count(p,
113 &command_error_count, 0);
114
115 col = -1;
116
117 shr_table_set_value_str(t, ++col, row,
118 libnvme_path_get_name(p), LEFT);
119 shr_table_set_value_str(t, ++col, row,
120 ana_state, LEFT);
121 shr_table_set_value_long(t, ++col, row,
122 command_retry_count, LEFT);
123 shr_table_set_value_long(t, ++col, row,
124 multipath_failover_count, LEFT);
125 shr_table_set_value_int(t, ++col, row,
126 command_error_count, LEFT);
127
128 shr_table_add_row(t, row);
129 }
130 }
131
132 shr_table_print_stream(stream, t);
133free_tbl:
134 shr_table_free(t);
135 return ret;
136}
137
138static int stdout_top_print_ctrl_summary(FILE *stream,
139 struct libnvme_subsystem *s, bool_Bool multipath)
140{
141 int ret = 0;
142 int row, col, npaths;
143 struct libnvme_ctrl *c;
144 struct libnvme_path *p;
145 struct libnvme_ns *n;
146 struct nvme_iostat aggr, st;
147 char r_bw_str[16], w_bw_str[16];
148 char r_iops_str[16], w_iops_str[16];
149 char r_clat_str[16], w_clat_str[16];
150 const char *node, *traddr, *state;
151 long reset_count, reconnect_count, command_error_count;
152 struct shr_table *t;
153 bool_Bool is_fabric = false0;
154 struct shr_table_column columns[] = {
155 {"Ctrl", LEFT, AUTO_WIDTH2147483647},
156 {"Paths", LEFT, AUTO_WIDTH2147483647},
157 {"Node", LEFT, AUTO_WIDTH2147483647},
158 {"Trtype", LEFT, AUTO_WIDTH2147483647},
159 {"Address", LEFT, AUTO_WIDTH2147483647},
160 {"State", LEFT, AUTO_WIDTH2147483647},
161 {"Resets", LEFT, AUTO_WIDTH2147483647},
162 {"Reconnects", LEFT, AUTO_WIDTH2147483647},
163 {"Errors", LEFT, AUTO_WIDTH2147483647},
164 {"r_IOPS", LEFT, 9},
165 {"w_IOPS", LEFT, 9},
166 {"r_clat", LEFT, 8},
167 {"w_clat", LEFT, 8},
168 {"r_bw", LEFT, 13},
169 {"w_bw", LEFT, 13},
170 {"Util%", LEFT, 6},
171 };
172
173 t = shr_table_create();
174 if (!t) {
175 nvme_show_error("Failed to init ctrl summary table")nvme_show_message(1, "Failed to init ctrl summary table");
176 return 1;
177 }
178
179 if (shr_table_add_columns_filter(t, columns, ARRAY_SIZE(columns)(sizeof(columns) / sizeof((columns)[0]) + 0),
180 stdout_top_print_ctrl_summary_tbl_filter, (void *)s) < 0) {
181 nvme_show_error("Failed to add columns to ctrl summary table")nvme_show_message(1, "Failed to add columns to ctrl summary table"
)
;
182 ret = 1;
183 goto free_tbl;
184 }
185
186 c = libnvme_subsystem_first_ctrl(s);
187 if (c)
188 is_fabric = stdout_top_nvme_ctrl_is_fabric(c);
189
190 fprintf(stream, "\n---------- Controller Summary --------\n\n");
191 libnvme_subsystem_for_each_ctrl(s, c)for (c = libnvme_subsystem_first_ctrl(s); c != ((void*)0); c =
libnvme_subsystem_next_ctrl(s, c))
{
192 npaths = 0;
193 aggr = (struct nvme_iostat){ 0 };
194
195 row = shr_table_get_row_id(t);
196 if (row < 0) {
197 nvme_show_error("Failed to add row to ctrl summary table")nvme_show_message(1, "Failed to add row to ctrl summary table"
)
;
198 ret = 1;
199 goto free_tbl;
200 }
201
202 if (multipath) {
203 libnvme_ctrl_for_each_path(c, p)for (p = libnvme_ctrl_first_path(c); p != ((void*)0); p = libnvme_ctrl_next_path
(c, p))
{
204
205 /* count num of paths per controller */
206 npaths++;
207
208 nvme_iostat_path(p, &st);
209 nvme_iostat_aggr(&aggr, &st);
210 }
211 } else {
212 libnvme_ctrl_for_each_ns(c, n)for (n = libnvme_ctrl_first_ns(c); n != ((void*)0); n = libnvme_ctrl_next_ns
(c, n))
{
213 nvme_iostat_ns(n, &st);
214 nvme_iostat_aggr(&aggr, &st);
215 }
216 }
217
218 nvme_iostat_format_iops(aggr.r_iops, r_iops_str,
219 sizeof(r_iops_str));
220 nvme_iostat_format_iops(aggr.w_iops, w_iops_str,
221 sizeof(w_iops_str));
222
223 nvme_iostat_format_bw(aggr.r_bw, r_bw_str, sizeof(r_bw_str));
224 nvme_iostat_format_bw(aggr.w_bw, w_bw_str, sizeof(w_bw_str));
225
226 nvme_iostat_format_lat(aggr.r_lat, r_clat_str,
227 sizeof(r_clat_str));
228 nvme_iostat_format_lat(aggr.w_lat, w_clat_str,
229 sizeof(w_clat_str));
230
231 libnvme_ctrl_get_numa_node(c, &node, "-1");
232 if (!strcmp(node, "-1"))
233 node = "NUMA_NO_NODE";
234
235 col = -1;
236
237 shr_table_set_value_str(t, ++col, row,
238 libnvme_ctrl_get_name(c), LEFT);
239 if (multipath)
240 shr_table_set_value_int(t, ++col, row, npaths, LEFT);
241
242 shr_table_set_value_str(t, ++col, row, node, LEFT);
243 shr_table_set_value_str(t, ++col, row,
244 libnvme_ctrl_get_transport(c), LEFT);
245 /*
246 * A loop controller has no transport address, and the state
247 * of a controller is gone while it is deleted.
248 */
249 traddr = libnvme_ctrl_get_traddr(c);
250 shr_table_set_value_str(t, ++col, row, traddr ? traddr : "",
251 LEFT);
252 state = libnvme_ctrl_get_state(c);
253 shr_table_set_value_str(t, ++col, row, state ? state : "",
254 LEFT);
255
256 libnvme_ctrl_get_reset_count(c, &reset_count, 0);
257 shr_table_set_value_long(t, ++col, row, reset_count, LEFT);
258
259 if (is_fabric) {
260 libnvme_ctrl_get_reconnect_count(c, &reconnect_count,
261 0);
262 shr_table_set_value_long(t, ++col, row,
263 reconnect_count, LEFT);
264 }
265
266 libnvme_ctrl_get_command_error_count(c, &command_error_count,
267 0);
268 shr_table_set_value_long(t, ++col, row, command_error_count, LEFT);
269 shr_table_set_value_str(t, ++col, row, r_iops_str, LEFT);
270 shr_table_set_value_str(t, ++col, row, w_iops_str, LEFT);
271 shr_table_set_value_str(t, ++col, row, r_clat_str, LEFT);
272 shr_table_set_value_str(t, ++col, row, w_clat_str, LEFT);
273 shr_table_set_value_str(t, ++col, row, r_bw_str, LEFT);
274 shr_table_set_value_str(t, ++col, row, w_bw_str, LEFT);
275 shr_table_set_value_double(t, ++col, row, aggr.util, LEFT);
276
277 shr_table_add_row(t, row);
278 }
279
280 shr_table_print_stream(stream, t);
281free_tbl:
282 shr_table_free(t);
283 return ret;
284}
285
286static int stdout_top_print_ns_stat(FILE *stream, struct libnvme_subsystem *s)
287{
288 int ret = 0;
289 struct libnvme_ns *n;
290 struct libnvme_ctrl *c;
291 int col, row;
292 struct nvme_iostat st;
293 char r_bw_str[16], w_bw_str[16];
294 char r_iops_str[16], w_iops_str[16];
295 char r_clat_str[16], w_clat_str[16];
296 struct shr_table *t;
297 struct shr_table_column columns[] = {
298 {"Namespace", LEFT, AUTO_WIDTH2147483647},
299 {"NSID", LEFT, AUTO_WIDTH2147483647},
300 {"Ctrl", LEFT, AUTO_WIDTH2147483647},
301 {"Retries", LEFT, AUTO_WIDTH2147483647},
302 {"Errors", LEFT, AUTO_WIDTH2147483647},
303 {"r_IOPS", LEFT, 9},
304 {"w_IOPS", LEFT, 9},
305 {"r_clat", LEFT, 8},
306 {"w_clat", LEFT, 8},
307 {"r_bw", LEFT, 13},
308 {"w_bw", LEFT, 13},
309 {"Inflights", LEFT, AUTO_WIDTH2147483647},
310 {"Util%", LEFT, 6},
311 };
312
313 t = shr_table_create();
314 if (!t) {
315 nvme_show_error("Failed to init ns stat table\n")nvme_show_message(1, "Failed to init ns stat table\n");
316 return 1;
317 }
318
319 if (shr_table_add_columns(t, columns, ARRAY_SIZE(columns)(sizeof(columns) / sizeof((columns)[0]) + 0)) < 0) {
320 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"
)
;
321 ret = 1;
322 goto free_tbl;
323 }
324
325 fprintf(stream, "----------- Namespace Stat -----------\n\n");
326 libnvme_subsystem_for_each_ctrl(s, c)for (c = libnvme_subsystem_first_ctrl(s); c != ((void*)0); c =
libnvme_subsystem_next_ctrl(s, c))
{
327 libnvme_ctrl_for_each_ns(c, n)for (n = libnvme_ctrl_first_ns(c); n != ((void*)0); n = libnvme_ctrl_next_ns
(c, n))
{
328 long command_retry_count, command_error_count;
329
330 nvme_iostat_ns(n, &st);
331
332 nvme_iostat_format_iops(st.r_iops, r_iops_str,
333 sizeof(r_iops_str));
334 nvme_iostat_format_iops(st.w_iops, w_iops_str,
335 sizeof(w_iops_str));
336
337 nvme_iostat_format_bw(st.r_bw, r_bw_str,
338 sizeof(r_bw_str));
339 nvme_iostat_format_bw(st.w_bw, w_bw_str,
340 sizeof(w_bw_str));
341
342 nvme_iostat_format_lat(st.r_lat, r_clat_str,
343 sizeof(r_clat_str));
344 nvme_iostat_format_lat(st.w_lat, w_clat_str,
345 sizeof(w_clat_str));
346
347 row = shr_table_get_row_id(t);
348 if (row < 0) {
349 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");
350 ret = 1;
351 goto free_tbl;
352 }
353
354 col = -1;
355
356 shr_table_set_value_str(t, ++col, row,
357 libnvme_ns_get_name(n), LEFT);
358 shr_table_set_value_int(t, ++col, row,
359 libnvme_ns_get_nsid(n), LEFT);
360 shr_table_set_value_str(t, ++col, row,
361 libnvme_ctrl_get_name(c), LEFT);
362 libnvme_ns_get_command_retry_count(n,
363 &command_retry_count, 0);
364 libnvme_ns_get_command_error_count(n,
365 &command_error_count, 0);
366 shr_table_set_value_long(t, ++col, row,
367 command_retry_count, LEFT);
368 shr_table_set_value_long(t, ++col, row,
369 command_error_count, LEFT);
370 shr_table_set_value_str(t, ++col, row, r_iops_str, LEFT);
371 shr_table_set_value_str(t, ++col, row, w_iops_str, LEFT);
372 shr_table_set_value_str(t, ++col, row, r_clat_str, LEFT);
373 shr_table_set_value_str(t, ++col, row, w_clat_str, LEFT);
374 shr_table_set_value_str(t, ++col, row, r_bw_str, LEFT);
375 shr_table_set_value_str(t, ++col, row, w_bw_str, LEFT);
376 shr_table_set_value_unsigned(t, ++col, row,
377 st.inflights, LEFT);
378 shr_table_set_value_double(t, ++col, row, st.util,
379 LEFT);
380
381 shr_table_add_row(t, row);
382 }
383 }
384
385 shr_table_print_stream(stream, t);
386free_tbl:
387 shr_table_free(t);
388 return ret;
389}
390
391static int stdout_top_print_nshead_stat(FILE *stream, struct libnvme_subsystem *s)
392{
393 int ret = 0;
394 struct libnvme_ns *n;
395 struct libnvme_path *p;
396 struct nvme_iostat st;
397 int col, row, npaths;
398 char r_iops_str[16], w_iops_str[16];
399 char r_clat_str[16], w_clat_str[16];
400 char r_bw_str[16], w_bw_str[16];
401 struct shr_table *t;
402 struct shr_table_column columns[] = {
403 {"NSHead", LEFT, AUTO_WIDTH2147483647},
404 {"NSID", LEFT, AUTO_WIDTH2147483647},
405 {"Paths", LEFT, AUTO_WIDTH2147483647},
406 {"Requeue-IO", LEFT, AUTO_WIDTH2147483647},
407 {"Fail-IO", LEFT, AUTO_WIDTH2147483647},
408 {"r_IOPS", LEFT, 9},
409 {"w_IOPS", LEFT, 9},
410 {"r_clat", LEFT, 8},
411 {"w_clat", LEFT, 8},
412 {"r_bw", LEFT, 13},
413 {"w_bw", LEFT, 13},
414 {"Inflights", LEFT, AUTO_WIDTH2147483647},
415 {"Util%", LEFT, 6},
416 };
417
418 t = shr_table_create();
419 if (!t) {
420 nvme_show_error("Failed to init nshead stat table\n")nvme_show_message(1, "Failed to init nshead stat table\n");
421 return 1;
422 }
423
424 if (shr_table_add_columns(t, columns, ARRAY_SIZE(columns)(sizeof(columns) / sizeof((columns)[0]) + 0)) < 0) {
425 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"
)
;
426 ret = 1;
427 goto free_tbl;
428 }
429
430 fprintf(stream, "------------ NSHead Stat -------------\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 long io_requeue_no_usable_path_count;
433 long io_fail_no_available_path_count;
434
435 npaths = 0;
436 nvme_iostat_ns(n, &st);
437
438 nvme_iostat_format_iops(st.r_iops, r_iops_str,
439 sizeof(r_iops_str));
440 nvme_iostat_format_iops(st.w_iops, w_iops_str,
441 sizeof(w_iops_str));
442
443 nvme_iostat_format_bw(st.r_bw, r_bw_str, sizeof(r_bw_str));
444 nvme_iostat_format_bw(st.w_bw, w_bw_str, sizeof(w_bw_str));
445
446 nvme_iostat_format_lat(st.r_lat, r_clat_str,
447 sizeof(r_clat_str));
448 nvme_iostat_format_lat(st.w_lat, w_clat_str,
449 sizeof(w_clat_str));
450
451 libnvme_namespace_for_each_path(n, p)for (p = libnvme_namespace_first_path(n); p != ((void*)0); p =
libnvme_namespace_next_path(n, p))
452 npaths++;
453
454 row = shr_table_get_row_id(t);
455 if (row < 0) {
456 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"
)
;
457 ret = 1;
458 goto free_tbl;
459 }
460
461 col = -1;
462
463 shr_table_set_value_str(t, ++col, row, libnvme_ns_get_name(n),
464 LEFT);
465 shr_table_set_value_int(t, ++col, row, libnvme_ns_get_nsid(n),
466 LEFT);
467 shr_table_set_value_int(t, ++col, row, npaths, LEFT);
468 libnvme_ns_get_io_requeue_no_usable_path_count(n,
469 &io_requeue_no_usable_path_count, 0);
470 libnvme_ns_get_io_fail_no_available_path_count(n,
471 &io_fail_no_available_path_count, 0);
472 shr_table_set_value_long(t, ++col, row,
473 io_requeue_no_usable_path_count, LEFT);
474 shr_table_set_value_long(t, ++col, row,
475 io_fail_no_available_path_count, LEFT);
476 shr_table_set_value_str(t, ++col, row, r_iops_str, LEFT);
477 shr_table_set_value_str(t, ++col, row, w_iops_str, LEFT);
478 shr_table_set_value_str(t, ++col, row, r_clat_str, LEFT);
479 shr_table_set_value_str(t, ++col, row, w_clat_str, LEFT);
480 shr_table_set_value_str(t, ++col, row, r_bw_str, LEFT);
481 shr_table_set_value_str(t, ++col, row, w_bw_str, LEFT);
482 shr_table_set_value_unsigned(t, ++col, row, st.inflights, LEFT);
483 shr_table_set_value_double(t, ++col, row, st.util, LEFT);
484
485 shr_table_add_row(t, row);
486 }
487
488 shr_table_print_stream(stream, t);
489free_tbl:
490 shr_table_free(t);
491 return ret;
492}
493
494static int stdout_top_print_path_perf(FILE *stream, struct libnvme_subsystem *s)
495{
496 int ret = 0;
497 struct libnvme_ns *n;
498 struct libnvme_path *p;
499 struct libnvme_ctrl *c;
500 int row, col;
501 struct nvme_iostat st;
502 char r_iops_str[16], w_iops_str[16];
503 char r_clat_str[16], w_clat_str[16];
504 char r_bw_str[16], w_bw_str[16];
505 bool_Bool first;
506 struct shr_table *t;
507 const char *iopolicy;
508 struct shr_table_column columns[] = {
509 {"NSHead", LEFT, AUTO_WIDTH2147483647},
510 {"NSID", LEFT, AUTO_WIDTH2147483647},
511 {"NSPath", LEFT, AUTO_WIDTH2147483647},
512 {"Nodes", LEFT, AUTO_WIDTH2147483647},
513 {"Qdepth", LEFT, AUTO_WIDTH2147483647},
514 {"Ctrl", LEFT, AUTO_WIDTH2147483647},
515 {"r_IOPS", LEFT, 9},
516 {"w_IOPS", LEFT, 9},
517 {"r_clat", LEFT, 8},
518 {"w_clat", LEFT, 8},
519 {"r_bw", LEFT, 13},
520 {"w_bw", LEFT, 13},
521 {"Inflights", LEFT, AUTO_WIDTH2147483647},
522 {"Util%", LEFT, 6},
523 };
524
525 t = shr_table_create();
526 if (!t) {
527 nvme_show_error("Failed to init path perf table")nvme_show_message(1, "Failed to init path perf table");
528 return 1;
529 }
530
531 if (shr_table_add_columns_filter(t, columns, ARRAY_SIZE(columns)(sizeof(columns) / sizeof((columns)[0]) + 0),
532 subsystem_iopolicy_filter, (void *)s) < 0) {
533 nvme_show_error("Failed to add columns to path perf table")nvme_show_message(1, "Failed to add columns to path perf table"
)
;
534 ret = 1;
535 goto free_tbl;
536 }
537
538 libnvme_subsystem_get_iopolicy(s, &iopolicy, "");
539
540 fprintf(stream, "\n---------- Path Performance ----------\n\n");
541 libnvme_subsystem_for_each_ns(s, n)for (n = libnvme_subsystem_first_ns(s); n != ((void*)0); n = libnvme_subsystem_next_ns
(s, n))
{
542 first = true1;
543 libnvme_namespace_for_each_path(n, p)for (p = libnvme_namespace_first_path(n); p != ((void*)0); p =
libnvme_namespace_next_path(n, p))
{
544 nvme_iostat_path(p, &st);
545
546 nvme_iostat_format_iops(st.r_iops, r_iops_str,
547 sizeof(r_iops_str));
548 nvme_iostat_format_iops(st.w_iops, w_iops_str,
549 sizeof(w_iops_str));
550
551 nvme_iostat_format_bw(st.r_bw, r_bw_str,
552 sizeof(r_bw_str));
553 nvme_iostat_format_bw(st.w_bw, w_bw_str,
554 sizeof(w_bw_str));
555
556 nvme_iostat_format_lat(st.r_lat, r_clat_str,
557 sizeof(r_clat_str));
558 nvme_iostat_format_lat(st.w_lat, w_clat_str,
559 sizeof(w_clat_str));
560
561 /* get controller associated with the path */
562 c = libnvme_path_get_ctrl(p);
563
564 row = shr_table_get_row_id(t);
565 if (row < 0) {
566 nvme_show_error("Failed to add row to path perf table")nvme_show_message(1, "Failed to add row to path perf table");
567 ret = 1;
568 goto free_tbl;
569 }
570
571 /*
572 * For the first row we print actual NSHead name,
573 * however, for the subsequent rows we print "arrow"
574 * ("-->") symbol for NSHead. This "arrow" style makes
575 * it visually obvious that subsequent entries (if
576 * present) are a path under the first NSHead.
577 */
578 col = -1;
579
580 if (first) {
581 shr_table_set_value_str(t, ++col, row,
582 libnvme_ns_get_name(n), LEFT);
583 first = false0;
584 } else
585 shr_table_set_value_str(t, ++col, row,
586 "-->", CENTERED);
587
588 shr_table_set_value_int(t, ++col, row,
589 libnvme_ns_get_nsid(n), CENTERED);
590 shr_table_set_value_str(t, ++col, row,
591 libnvme_path_get_name(p), LEFT);
592
593 if (!strcmp(iopolicy, "numa")) {
594 const char *numa_nodes;
595
596 libnvme_path_get_numa_nodes(p, &numa_nodes, "");
597 shr_table_set_value_str(t, ++col, row,
598 numa_nodes, CENTERED);
599 } else if (!strcmp(iopolicy, "queue-depth")) {
600 int queue_depth;
601
602 libnvme_path_get_queue_depth(p, &queue_depth,
603 0);
604 shr_table_set_value_int(t, ++col, row,
605 queue_depth, CENTERED);
606 }
607
608 shr_table_set_value_str(t, ++col, row,
609 libnvme_ctrl_get_name(c), LEFT);
610 shr_table_set_value_str(t, ++col, row, r_iops_str, LEFT);
611 shr_table_set_value_str(t, ++col, row, w_iops_str, LEFT);
612 shr_table_set_value_str(t, ++col, row, r_clat_str, LEFT);
613 shr_table_set_value_str(t, ++col, row, w_clat_str, LEFT);
614 shr_table_set_value_str(t, ++col, row, r_bw_str, LEFT);
615 shr_table_set_value_str(t, ++col, row, w_bw_str, LEFT);
616 shr_table_set_value_unsigned(t, ++col, row,
617 st.inflights, LEFT);
618 shr_table_set_value_double(t, ++col, row, st.util,
619 LEFT);
620
621 shr_table_add_row(t, row);
622 }
623 }
624 shr_table_print_stream(stream, t);
625free_tbl:
626 shr_table_free(t);
627 return ret;
628}
629
630static void stdout_top_print_subsys_topology_config(FILE *stream,
631 struct libnvme_subsystem *s)
632{
633 int len = strlen(libnvme_subsystem_get_name(s));
634 const char *iopolicy;
635 const char *model;
636 const char *serial;
637 const char *firmware;
638
639 libnvme_subsystem_get_iopolicy(s, &iopolicy, "");
640 libnvme_subsystem_get_model(s, &model, "undefined");
641 libnvme_subsystem_get_serial(s, &serial, "");
642 libnvme_subsystem_get_firmware(s, &firmware, "");
643
644 fprintf(stream, "%s - NQN=%s\n", libnvme_subsystem_get_name(s),
645 libnvme_subsystem_get_subsysnqn(s));
646 fprintf(stream, "%*s hostnqn=%s\n", len, " ",
647 libnvme_host_get_hostnqn(libnvme_subsystem_get_host(s)));
648 fprintf(stream, "%*s iopolicy=%s\n", len, " ", iopolicy);
649
650 fprintf(stream, "%*s model=%s\n", len, " ", model);
651 fprintf(stream, "%*s serial=%s\n", len, " ", serial);
652 fprintf(stream, "%*s firmware=%s\n", len, " ", firmware);
653 fprintf(stream, "%*s type=%s\n", len, " ",
654 libnvme_subsystem_get_subsystype(s));
655
656 fprintf(stream, "\n");
657}
658
659static void stdout_top_update_stat(struct libnvme_subsystem *s)
660{
661 nvme_iostat_update_subsys(s, nvme_is_multipath(s));
662}
663
664static void stdout_top_reset_stat(struct libnvme_subsystem *s)
665{
666 nvme_iostat_reset_subsys(s, nvme_is_multipath(s));
667}
668
669static int stdout_top_print_subsys_topology(struct dashboard_ctx *db_ctx,
670 FILE *stream, struct libnvme_subsystem *s)
671{
672 int ret = 0;
673 bool_Bool multipath = nvme_is_multipath(s);
674
675 stdout_top_update_stat(s);
676
677 stdout_top_print_subsys_topology_config(stream, s);
678
679 if (multipath) {
680 ret = stdout_top_print_nshead_stat(stream, s);
681 if (ret)
682 return ret;
683
684 ret = stdout_top_print_path_perf(stream, s);
685 if (ret)
686 return ret;
687
688 ret = stdout_top_print_path_health(stream, s);
689 if (ret)
690 return ret;
691 } else {
692 ret = stdout_top_print_ns_stat(stream, s);
693 if (ret)
694 return ret;
695 }
696
697 ret = stdout_top_print_ctrl_summary(stream, s, multipath);
698
699 return ret;
700}
701
702static void stdout_top_print_subsys_topology_header(
703 struct dashboard_ctx *db_ctx, FILE *stream)
704{
705 fprintf(stream, "---- nvme-top - Refresh: %d Second ----\n",
706 dashboard_get_interval(db_ctx));
707
708 dashboard_set_header_rows(db_ctx, 1);
709
710 /* highlight the header row */
711 dashboard_set_header_row_reverse(db_ctx, 0);
712}
713
714static void stdout_top_print_subsys_topology_footer(
715 struct dashboard_ctx *db_ctx, FILE *stream)
716{
717 fprintf(stream, "\n--------------------------------------\n");
718 fprintf(stream, "[up/down keys to navigate, ESC to go back to the previous screen, q to quit]\n");
719
720 dashboard_set_footer_rows(db_ctx, 3);
721
722 /* hightligh the last footer row */
723 dashboard_set_footer_row_reverse(db_ctx, 2);
724}
725
726static struct libnvme_subsystem *stdout_top_search_subsystem(
727 struct libnvme_global_ctx *ctx, const char *subsys_name)
728{
729 struct libnvme_host *h;
730 struct libnvme_subsystem *s;
731
732 libnvme_for_each_host(ctx, h)for (h = libnvme_first_host(ctx); h != ((void*)0); h = libnvme_next_host
(ctx, h))
{
733 libnvme_for_each_subsystem(h, s)for (s = libnvme_first_subsystem(h); s != ((void*)0); s = libnvme_next_subsystem
(h, s))
{
734 if (!strcmp(libnvme_subsystem_get_name(s), subsys_name))
735 return s;
736 }
737 }
738
739 return NULL((void*)0);
740}
741
742static struct libnvme_subsystem **stdout_top_build_subsys_arr(
743 struct libnvme_global_ctx *ctx, int *num_subsys)
744{
745 struct libnvme_host *h;
746 struct libnvme_subsystem *s;
747 struct libnvme_subsystem **subsys_arr;
748 int subsys_idx = 0;
749 int n = 0;
750
751 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 754
752 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 751
753 n++;
754 if (!n
13.1
'n' is 1
) {
14
Taking false branch
755 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"
)
;
756 return NULL((void*)0);
757 }
758
759 subsys_arr = calloc(n, sizeof(struct libnvme_subsystem *));
15
Memory is allocated
760 if (!subsys_arr) {
16
Assuming 'subsys_arr' is non-null
17
Taking false branch
761 nvme_show_error("Failed to allocate memory for subsys array\n")nvme_show_message(1, "Failed to allocate memory for subsys array\n"
)
;
762 return NULL((void*)0);
763 }
764
765 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 770
766 libnvme_for_each_subsystem(h, s)for (s = libnvme_first_subsystem(h); s != ((void*)0); s = libnvme_next_subsystem
(h, s))
767 subsys_arr[subsys_idx++] = s;
768 }
769
770 *num_subsys = n;
771 return subsys_arr;
772}
773
774static int stdout_top_find_subsys_by_name(struct libnvme_subsystem **subsys_arr,
775 int num_subsys, const char *subsys_name)
776{
777 int idx;
778 struct libnvme_subsystem *s;
779
780 for (idx = 0; idx < num_subsys; idx++) {
781 s = subsys_arr[idx];
782
783 if (!strcmp(libnvme_subsystem_get_name(s), subsys_name))
784 return idx;
785 }
786
787 return -1;
788}
789
790static int handle_event_page_down(struct dashboard_ctx *db_ctx)
791{
792 int data_start, data_rows, frame_rows;
793 int scroll_down, scroll = 0;
794
795 data_start = dashboard_get_data_start(db_ctx);
796 data_rows = dashboard_get_data_rows(db_ctx);
797 frame_rows = dashboard_get_frame_data_rows(db_ctx);
798
799 /*
800 * Scroll down max up to one page frame from current data_start index.
801 * While scrolling down, ensure we don't move past the max available
802 * data rows. If we are already on the last page frame or there's no
803 * data if move past the current page frame then ignore the key press.
804 */
805 scroll_down = data_start + frame_rows;
806
807 if (scroll_down < data_rows) {
808 dashboard_set_data_start(db_ctx, scroll_down);
809 scroll = 1;
810 }
811
812 return scroll;
813}
814
815static int handle_event_page_up(struct dashboard_ctx *db_ctx)
816{
817 int data_start, frame_rows, off;
818 int scroll_up, scroll = 0;
819
820 data_start = dashboard_get_data_start(db_ctx);
821 frame_rows = dashboard_get_frame_data_rows(db_ctx);
822
823 /*
824 * Scroll back up max up to one page frame. Determine the num of data
825 * rows we can scroll back up based on current data start index and max
826 * num of rows which could be drawn in one page frame. If we're already
827 * on the first page frame and hence we can't scroll back then ignore
828 * the key press.
829 */
830 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; })
;
831 scroll_up = data_start - off;
832
833 if (scroll_up != data_start) {
834 dashboard_set_data_start(db_ctx, scroll_up);
835 scroll = 1;
836 }
837
838 return scroll;
839}
840
841/*
842 * Draws subsys topology screen of susbystem @s
843 * Returns: 0 if ESC key is pressed or needs to draw subsystem selection screen
844 * 1 if 'q' is pressed or in case of error
845 */
846static int stdout_top_draw_subsys_topology_screen(
847 struct libnvme_global_ctx *ctx, struct dashboard_ctx *db_ctx,
848 FILE *stream, struct libnvme_subsystem *_s)
849{
850 enum event_type event;
851 int ret, scroll = 0;
852 int data_start, data_rows;
853 __cleanup_free__attribute__((cleanup(shr_freep))) const char *subsys_name;
854 struct libnvme_subsystem *s = _s;
855
856 subsys_name = strdup(libnvme_subsystem_get_name(s));
857 if (!subsys_name)
858 return 1; /* force quit */
859
860 while (1) {
861 stdout_top_print_subsys_topology_header(db_ctx, stream);
862 ret = stdout_top_print_subsys_topology(db_ctx, stream, s);
863 if (ret)
864 break;
865 stdout_top_print_subsys_topology_footer(db_ctx, stream);
866
867draw:
868 ret = dashboard_draw_frame(db_ctx, scroll);
869 if (ret)
870 break;
871wait_for_event:
872 event = dashboard_wait_for_event(db_ctx);
873 if (event == EVENT_TYPE_KEY_ESC) {
874 ret = 0;
875 dashboard_reset(db_ctx);
876 break;
877 } else if (event == EVENT_TYPE_KEY_UP) {
878 data_start = dashboard_get_data_start(db_ctx);
879 /*
880 * If we don't move past the first data row by shifting
881 * one data row up then do so, otherwise ignore the key
882 * press.
883 */
884 if (data_start - 1 >= 0) {
885 dashboard_set_data_start(db_ctx,
886 data_start - 1);
887 scroll = 1;
888 goto draw;
889 }
890 goto wait_for_event;
891 } else if (event == EVENT_TYPE_KEY_DOWN) {
892 data_start = dashboard_get_data_start(db_ctx);
893 data_rows = dashboard_get_data_rows(db_ctx);
894 /*
895 * If we don't move past the max data rows shifting one
896 * row down then do so, otherwise ignore the key press.
897 */
898 if (data_start + 1 < data_rows) {
899 dashboard_set_data_start(db_ctx,
900 data_start + 1);
901 scroll = 1;
902 goto draw;
903 }
904 goto wait_for_event;
905 } else if (event == EVENT_TYPE_TIMEOUT) { /* screen timed out */
906 scroll = 0;
907 } else if (event == EVENT_TYPE_KEY_QUIT ||
908 event == EVENT_TYPE_ERROR) {
909 ret = 1;
910 break;
911 } else if (event == EVENT_TYPE_NVME_UEVENT) {
912
913 if (libnvme_refresh_topology(ctx)) {
914 ret = 1; /* force quit */
915 break;
916 }
917
918 s = stdout_top_search_subsystem(ctx, subsys_name);
919 if (!s) {
920 ret = 0; /* draw subsys selection screen */
921 break;
922 }
923 scroll = 0;
924 } else if (event == EVENT_TYPE_SIGWINCH) {
925 /*
926 * Window size would have changed so re-draw the subsys
927 * topology screen.
928 */
929 scroll = 0;
930 } else if (event == EVENT_TYPE_KEY_PAGE_DOWN) {
931
932 scroll = handle_event_page_down(db_ctx);
933 if (scroll)
934 goto draw;
935
936 goto wait_for_event;
937
938 } else if (event == EVENT_TYPE_KEY_PAGE_UP) {
939
940 scroll = handle_event_page_up(db_ctx);
941 if (scroll)
942 goto draw;
943
944 goto wait_for_event;
945 } /* else ignore */
946 }
947
948 return ret;
949}
950
951static int stdout_top_draw_subsys_screen(struct dashboard_ctx *db_ctx,
952 FILE *stream, struct libnvme_subsystem **subsys_arr, int num_subsys)
953{
954 int ret = 0;
955 struct libnvme_subsystem *s;
956 struct libnvme_ctrl *c;
957 struct libnvme_ns *n;
958 struct libnvme_path *p;
959 int i, row, col, num_ns, num_path, num_ctrl;
960 struct nvme_iostat aggr, st;
961 char r_bw_str[16], w_bw_str[16];
962 char r_iops_str[16], w_iops_str[16];
963 char r_clat_str[16], w_clat_str[16];
964 const char *iopolicy;
965 struct shr_table *t;
966 struct shr_table_column columns[] = {
967 {"Subsystem", LEFT, AUTO_WIDTH2147483647},
968 {"Namespaces", LEFT, AUTO_WIDTH2147483647},
969 {"Paths", LEFT, AUTO_WIDTH2147483647},
970 {"Ctrls", LEFT, AUTO_WIDTH2147483647},
971 {"IOPolicy", LEFT, AUTO_WIDTH2147483647},
972 {"r_IOPS", LEFT, 9},
973 {"w_IOPS", LEFT, 9},
974 {"r_clat", LEFT, 8},
975 {"w_clat", LEFT, 8},
976 {"r_bw", LEFT, 13},
977 {"w_bw", LEFT, 13},
978 {"Util%", LEFT, 6},
979 };
980
981 fprintf(stream, "---- nvme-top - Refresh: %d Second ----\n",
982 dashboard_get_interval(db_ctx));
983 fprintf(stream, "\n--------- Subsystem Summary ----------\n\n");
984
985 t = shr_table_create();
986 if (!t) {
987 nvme_show_error("Failed to init subsys screen table\n")nvme_show_message(1, "Failed to init subsys screen table\n");
988 return -1;
989 }
990
991 if (shr_table_add_columns(t, columns, ARRAY_SIZE(columns)(sizeof(columns) / sizeof((columns)[0]) + 0)) < 0) {
992 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"
)
;
993 ret = -1;
994 goto free_tbl;
995 }
996 /*
997 * Header row count is calculated manually. The first row displays the
998 * refresh interval, followed by an empty row. The third row displays
999 * the heading followed by another empty row. The fifth row is for
1000 * displaying shr_table columns and then another row for dashes underneath
1001 * the shr_table columns.
1002 */
1003 dashboard_set_header_rows(db_ctx, 6);
1004
1005 /* highlight the first header row */
1006 dashboard_set_header_row_reverse(db_ctx, 0);
1007
1008 for (i = 0; i < num_subsys; i++) {
1009 s = subsys_arr[i];
1010 num_ctrl = num_ns = num_path = 0;
1011 aggr = (struct nvme_iostat){ 0 };
1012 libnvme_subsystem_get_iopolicy(s, &iopolicy, "NA");
1013
1014 libnvme_subsystem_for_each_ctrl(s, c)for (c = libnvme_subsystem_first_ctrl(s); c != ((void*)0); c =
libnvme_subsystem_next_ctrl(s, c))
1015 num_ctrl++;
1016
1017 stdout_top_update_stat(s);
1018
1019 if (nvme_is_multipath(s)) {
1020 libnvme_subsystem_for_each_ns(s, n)for (n = libnvme_subsystem_first_ns(s); n != ((void*)0); n = libnvme_subsystem_next_ns
(s, n))
{
1021 num_ns++;
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 num_path++;
1025
1026 nvme_iostat_ns(n, &st);
1027 nvme_iostat_aggr(&aggr, &st);
1028 }
1029 } else {
1030 libnvme_subsystem_for_each_ctrl(s, c)for (c = libnvme_subsystem_first_ctrl(s); c != ((void*)0); c =
libnvme_subsystem_next_ctrl(s, c))
{
1031 libnvme_ctrl_for_each_ns(c, n)for (n = libnvme_ctrl_first_ns(c); n != ((void*)0); n = libnvme_ctrl_next_ns
(c, n))
{
1032 num_ns++;
1033
1034 nvme_iostat_ns(n, &st);
1035 nvme_iostat_aggr(&aggr, &st);
1036 }
1037 }
1038 }
1039
1040 nvme_iostat_format_iops(aggr.r_iops, r_iops_str,
1041 sizeof(r_iops_str));
1042 nvme_iostat_format_iops(aggr.w_iops, w_iops_str,
1043 sizeof(w_iops_str));
1044
1045 nvme_iostat_format_bw(aggr.r_bw, r_bw_str, sizeof(r_bw_str));
1046 nvme_iostat_format_bw(aggr.w_bw, w_bw_str, sizeof(w_bw_str));
1047
1048 nvme_iostat_format_lat(aggr.r_lat, r_clat_str,
1049 sizeof(r_clat_str));
1050 nvme_iostat_format_lat(aggr.w_lat, w_clat_str,
1051 sizeof(w_clat_str));
1052
1053 row = shr_table_get_row_id(t);
1054 if (row < 0) {
1055 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"
)
;
1056 ret = -1;
1057 goto free_tbl;
1058 }
1059
1060 col = -1;
1061
1062 shr_table_set_value_str(t, ++col, row,
1063 libnvme_subsystem_get_name(s), LEFT);
1064 shr_table_set_value_int(t, ++col, row, num_ns, LEFT);
1065 shr_table_set_value_int(t, ++col, row, num_path, LEFT);
1066 shr_table_set_value_int(t, ++col, row, num_ctrl, LEFT);
1067 shr_table_set_value_str(t, ++col, row, iopolicy, LEFT);
1068 shr_table_set_value_str(t, ++col, row, r_iops_str, LEFT);
1069 shr_table_set_value_str(t, ++col, row, w_iops_str, LEFT);
1070 shr_table_set_value_str(t, ++col, row, r_clat_str, LEFT);
1071 shr_table_set_value_str(t, ++col, row, w_clat_str, LEFT);
1072 shr_table_set_value_str(t, ++col, row, r_bw_str, LEFT);
1073 shr_table_set_value_str(t, ++col, row, w_bw_str, LEFT);
1074 shr_table_set_value_double(t, ++col, row, aggr.util, LEFT);
1075
1076 shr_table_add_row(t, row);
1077 }
1078
1079 shr_table_print_stream(stream, t);
1080
1081 fprintf(stream, "\n--------------------------------------\n");
1082 fprintf(stream, "[up/down keys to navigate, Enter to view, q to quit]\n");
1083
1084 /*
1085 * Footer rows are calculated manually.
1086 * The first row is empty (adds spaces) followed by another row for
1087 * dashes and the last row adds footer string.
1088 */
1089 dashboard_set_footer_rows(db_ctx, 3);
1090
1091 /* highlight the last footer row */
1092 dashboard_set_footer_row_reverse(db_ctx, 2);
1093
1094free_tbl:
1095 shr_table_free(t);
1096 return ret;
1097}
1098
1099void stdout_top(int refresh_interval)
1100{
1101 FILE *stream;
1102 enum event_type event;
1103 struct dashboard_ctx *db_ctx;
1104 struct libnvme_host *h;
1105 struct libnvme_subsystem *s;
1106 __cleanup_nvme_global_ctx__attribute__((cleanup(cleanup_nvme_global_ctx))) struct libnvme_global_ctx *ctx = NULL((void*)0);
1107 __cleanup_free__attribute__((cleanup(shr_freep))) struct libnvme_subsystem **subsys_arr = NULL((void*)0);
1108 int data_start, frame_rows, quit = 0, scroll = 0;
1109 int num_subsys = 0, subsys_idx = 0;
1110 int err;
1111
1112 err = nvme_create_global_ctx(&ctx);
1113 if (err)
1
Assuming 'err' is 0
2
Taking false branch
1114 return;
1115
1116 if (libnvme_scan_topology(ctx, NULL((void*)0), NULL((void*)0))) {
3
Assuming the condition is false
4
Taking false branch
1117 nvme_show_error("Failed to scan topology")nvme_show_message(1, "Failed to scan topology");
1118 return;
1119 }
1120
1121 subsys_arr = stdout_top_build_subsys_arr(ctx, &num_subsys);
5
Calling 'stdout_top_build_subsys_arr'
20
Returned allocated memory
1122 if (!subsys_arr
20.1
'subsys_arr' is non-null
)
21
Taking false branch
1123 return;
1124
1125 stream = dashboard_init(&db_ctx, refresh_interval);
1126 if (!stream)
22
Assuming 'stream' is null
23
Taking true branch
1127 return;
24
Potential leak of memory pointed to by 'subsys_arr'
1128
1129 libnvme_for_each_host(ctx, h)for (h = libnvme_first_host(ctx); h != ((void*)0); h = libnvme_next_host
(ctx, h))
{
1130 libnvme_for_each_subsystem(h, s)for (s = libnvme_first_subsystem(h); s != ((void*)0); s = libnvme_next_subsystem
(h, s))
1131 stdout_top_reset_stat(s);
1132 }
1133
1134 /*
1135 * We start with first subsystem highlited, so set subsystem index to 0.
1136 */
1137 subsys_idx = 0;
1138 while (!quit) {
1139 if (stdout_top_draw_subsys_screen(db_ctx, stream, subsys_arr,
1140 num_subsys) < 0)
1141 break;
1142draw:
1143 /* highlight the selected @subsys_idx row */
1144 dashboard_set_data_row_reverse(db_ctx, subsys_idx);
1145 if (dashboard_draw_frame(db_ctx, scroll) < 0)
1146 break;
1147wait_for_event:
1148 event = dashboard_wait_for_event(db_ctx);
1149 switch (event) {
1150 case EVENT_TYPE_KEY_QUIT:
1151 case EVENT_TYPE_ERROR:
1152 quit = 1;
1153 break;
1154 case EVENT_TYPE_KEY_RETURN: {
1155 dashboard_reset(db_ctx);
1156 s = subsys_arr[subsys_idx];
1157
1158 quit = stdout_top_draw_subsys_topology_screen(ctx,
1159 db_ctx, stream, s);
1160 if (quit)
1161 break;
1162
1163 scroll = 0;
1164 free(subsys_arr);
1165 subsys_arr = NULL((void*)0);
1166
1167 if (libnvme_refresh_topology(ctx)) {
1168 quit = 1;
1169 break;
1170 }
1171
1172 /*
1173 * The topology may have changed while the subsystem
1174 * dashboard was active. Restart from the first
1175 * subsystem instead of trying to restore the previous
1176 * screen position.
1177 */
1178 subsys_idx = 0;
1179 subsys_arr = stdout_top_build_subsys_arr(ctx,
1180 &num_subsys);
1181 if (!subsys_arr)
1182 quit = 1;
1183
1184 break;
1185 }
1186 case EVENT_TYPE_NVME_UEVENT: {
1187 __cleanup_free__attribute__((cleanup(shr_freep))) char *subsys_name = NULL((void*)0);
1188 struct libnvme_subsystem *s = subsys_arr[subsys_idx];
1189
1190 subsys_name = strdup(libnvme_subsystem_get_name(s));
1191 if (!subsys_name) {
1192 quit = 1;
1193 break;
1194 }
1195
1196 free(subsys_arr);
1197 subsys_arr = NULL((void*)0);
1198
1199 if (libnvme_refresh_topology(ctx)) {
1200 quit = 1;
1201 break;
1202 }
1203
1204 subsys_arr = stdout_top_build_subsys_arr(ctx,
1205 &num_subsys);
1206 if (!subsys_arr) {
1207 quit = 1;
1208 break;
1209 }
1210
1211 subsys_idx = stdout_top_find_subsys_by_name(subsys_arr,
1212 num_subsys, subsys_name);
1213 if (subsys_idx < 0)
1214 subsys_idx = 0;
1215
1216 scroll = 0;
1217 break;
1218 }
1219 case EVENT_TYPE_KEY_DOWN:
1220 /*
1221 * The @num_subsys should be equal to @data_rows, so we
1222 * evaluate here that we don't move pass the last data
1223 * row (or the last subsys) if we were to shift (focus)
1224 * one row down. In case it's not possible to shift
1225 * because we are already down to the last row then
1226 * ignore key press.
1227 */
1228 if (subsys_idx + 1 < num_subsys) {
1229 subsys_idx++; /* we'll highlight this row */
1230
1231 data_start = dashboard_get_data_start(db_ctx);
1232 frame_rows = dashboard_get_frame_data_rows(
1233 db_ctx);
1234 /*
1235 * If moving to next row requires shifting the
1236 * window frame buffer by one position down then
1237 * do so.
1238 */
1239 if (subsys_idx >= data_start + frame_rows) {
1240 dashboard_set_data_start(db_ctx,
1241 data_start + 1);
1242 }
1243 /*
1244 * As we are scrolling one row down, we need to
1245 * re-draw the frame.
1246 */
1247 scroll = 1;
1248 goto draw;
1249 }
1250 goto wait_for_event;
1251 case EVENT_TYPE_KEY_UP:
1252 /*
1253 * If it's possible to move one row above the current
1254 * subsys (higlighted) row then decrease the subsys_idx
1255 * by one.
1256 */
1257 if (subsys_idx - 1 >= 0) {
1258 subsys_idx--;
1259 /*
1260 * If moving one row up requires us to shift
1261 * the window frame buffer by one position up
1262 * then do so.
1263 */
1264 data_start = dashboard_get_data_start(db_ctx);
1265 if (subsys_idx < data_start) {
1266 dashboard_set_data_start(db_ctx,
1267 data_start - 1);
1268 }
1269 /*
1270 * As we are scrolling one row up, we need to
1271 * re-draw the frame.
1272 */
1273 scroll = 1;
1274 goto draw;
1275 }
1276 goto wait_for_event;
1277 case EVENT_TYPE_TIMEOUT:
1278 /* subsys screen timed out */
1279 scroll = 0;
1280 break;
1281 case EVENT_TYPE_SIGWINCH:
1282 /*
1283 * Window size would have changed so re-draw the subsys
1284 * selection screen.
1285 */
1286 scroll = 0;
1287 break;
1288 case EVENT_TYPE_KEY_PAGE_DOWN:
1289 scroll = handle_event_page_down(db_ctx);
1290 if (scroll) {
1291 subsys_idx = dashboard_get_data_start(db_ctx);
1292 goto draw;
1293 }
1294
1295 goto wait_for_event;
1296 case EVENT_TYPE_KEY_PAGE_UP:
1297 scroll = handle_event_page_up(db_ctx);
1298 if (scroll) {
1299 subsys_idx = dashboard_get_data_start(db_ctx);
1300 goto draw;
1301 }
1302
1303 goto wait_for_event;
1304 default: /* unknown event, ignore */
1305 continue;
1306 }
1307 }
1308
1309 dashboard_exit(db_ctx);
1310}