Bug Summary

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

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple x86_64-redhat-linux-gnu -O3 -analyze -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name nvme-print-stdout-top.c -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -mrelocation-model static -mframe-pointer=none -fmath-errno -ffp-contract=on -fno-rounding-math -mconstructor-aliases -funwind-tables=2 -target-cpu x86-64 -tune-cpu generic -debugger-tuning=gdb -fdebug-compilation-dir=/__w/nvme-cli/nvme-cli/.build-ci -fcoverage-compilation-dir=/__w/nvme-cli/nvme-cli/.build-ci -resource-dir /usr/bin/../lib/clang/22 -include /__w/nvme-cli/nvme-cli/.build-ci/nvme-config.h -I nvme.p -I . -I .. -I src -I ../src -I ccan -I ../ccan -I libnvme/src -I ../libnvme/src -I /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))
752 libnvme_for_each_subsystem(h, s)for (s = libnvme_first_subsystem(h); s != ((void*)0); s = libnvme_next_subsystem
(h, s))
753 n++;
754 if (!n) {
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 *));
760 if (!subsys_arr) {
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))
{
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));
23
Memory is allocated
857 if (!subsys_name)
24
Assuming 'subsys_name' is non-null
25
Taking false branch
858 return 1; /* force quit */
859
860 while (1) {
26
Loop condition is true. Entering loop body
861 stdout_top_print_subsys_topology_header(db_ctx, stream);
862 ret = stdout_top_print_subsys_topology(db_ctx, stream, s);
863 if (ret
26.1
'ret' is 1
)
27
Taking true branch
864 break;
28
Execution jumps to the end of the function
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;
29
Potential leak of memory pointed to by 'subsys_name'
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);
1122 if (!subsys_arr
4.1
'subsys_arr' is non-null
)
5
Taking false branch
1123 return;
1124
1125 stream = dashboard_init(&db_ctx, refresh_interval);
1126 if (!stream)
6
Assuming 'stream' is non-null
7
Taking false branch
1127 return;
1128
1129 libnvme_for_each_host(ctx, h)for (h = libnvme_first_host(ctx); h != ((void*)0); h = libnvme_next_host
(ctx, h))
{
8
Assuming 'h' is not equal to null
9
Loop condition is true. Entering loop body
14
Assuming 'h' is equal to null
15
Loop condition is false. Execution continues on line 1137
1130 libnvme_for_each_subsystem(h, s)for (s = libnvme_first_subsystem(h); s != ((void*)0); s = libnvme_next_subsystem
(h, s))
10
Assuming 's' is not equal to null
11
Loop condition is true. Entering loop body
12
Assuming 's' is equal to null
13
Loop condition is false. Execution continues on line 1129
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) {
16
Loop condition is true. Entering loop body
1139 if (stdout_top_draw_subsys_screen(db_ctx, stream, subsys_arr,
17
Assuming the condition is false
18
Taking false branch
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)
19
Assuming the condition is false
20
Taking false branch
1146 break;
1147wait_for_event:
1148 event = dashboard_wait_for_event(db_ctx);
1149 switch (event) {
21
Control jumps to 'case EVENT_TYPE_KEY_RETURN:' at line 1154
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,
22
Calling 'stdout_top_draw_subsys_topology_screen'
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}