Files
qdl/ux.c
Bjorn Andersson 156d9673a2 ramdump: Provide some user feedback
Dumping all the DDR takes significant time and there's currently no
user-visible feedback provided to indicate that even the process has
started.

Solve this by wiring up the ux module and provide a progress bar while
dumping segments, as well as information as the segments are skipped or
dumped.

Add missing ux_init() to the ramdump setup, and make sure to clamp value
to max in the progress calculation, to avoid funky issues when progress
is made beyond the size of the chunk.

Signed-off-by: Bjorn Andersson <bjorn.andersson@oss.qualcomm.com>
2026-02-02 12:23:24 -06:00

175 lines
3.3 KiB
C

// SPDX-License-Identifier: BSD-3-Clause
#include <stdarg.h>
#ifdef _WIN32
#include <windows.h>
#else
#include <sys/ioctl.h>
#endif
#include <sys/time.h>
#include <unistd.h>
#include "qdl.h"
#define UX_PROGRESS_REFRESH_RATE 10
#define UX_PROGRESS_SIZE_MAX 80
#define HASHES "################################################################################"
#define DASHES "--------------------------------------------------------------------------------"
static const char * const progress_hashes = HASHES;
static const char * const progress_dashes = DASHES;
static unsigned int ux_width;
static unsigned int ux_cur_line_length;
/*
* Levels of output:
*
* error: used to signal errors to the user
* info: used to inform the user about progress
* logs: log prints from the device
* debug: protocol logs
*/
/* Clear ux_cur_line_length characters of the progress bar from the screen */
static void ux_clear_line(void)
{
if (!ux_cur_line_length)
return;
printf("%*s\r", ux_cur_line_length, "");
fflush(stdout);
ux_cur_line_length = 0;
}
#ifdef _WIN32
void ux_init(void)
{
CONSOLE_SCREEN_BUFFER_INFO csbi;
int columns;
HANDLE stdoutHandle = GetStdHandle(STD_OUTPUT_HANDLE);
if (GetConsoleScreenBufferInfo(stdoutHandle, &csbi)) {
columns = csbi.srWindow.Right - csbi.srWindow.Left + 1;
ux_width = MIN(columns, UX_PROGRESS_SIZE_MAX);
}
}
#else
void ux_init(void)
{
struct winsize w;
int ret;
ret = ioctl(STDOUT_FILENO, TIOCGWINSZ, &w);
if (!ret)
ux_width = MIN(w.ws_col, UX_PROGRESS_SIZE_MAX);
}
#endif
void ux_err(const char *fmt, ...)
{
va_list ap;
ux_clear_line();
va_start(ap, fmt);
vfprintf(stderr, fmt, ap);
va_end(ap);
fflush(stderr);
}
void ux_info(const char *fmt, ...)
{
va_list ap;
ux_clear_line();
va_start(ap, fmt);
vprintf(fmt, ap);
va_end(ap);
fflush(stdout);
}
void ux_log(const char *fmt, ...)
{
va_list ap;
if (!qdl_debug)
return;
ux_clear_line();
va_start(ap, fmt);
vprintf(fmt, ap);
va_end(ap);
fflush(stdout);
}
void ux_debug(const char *fmt, ...)
{
va_list ap;
if (!qdl_debug)
return;
ux_clear_line();
va_start(ap, fmt);
vprintf(fmt, ap);
va_end(ap);
fflush(stdout);
}
void ux_progress(const char *fmt, unsigned int value, unsigned int max, ...)
{
static struct timeval last_progress_update;
unsigned long elapsed_us;
unsigned int bar_length;
unsigned int bars;
unsigned int dashes;
struct timeval now;
char task_name[32];
float percent;
va_list ap;
/* Don't print progress is window is too narrow, or if stdout is redirected */
if (ux_width < 30)
return;
/* Avoid updating the console more than UX_PROGRESS_REFRESH_RATE per second */
if (last_progress_update.tv_sec) {
gettimeofday(&now, NULL);
elapsed_us = (now.tv_sec - last_progress_update.tv_sec) * 1000000 +
(now.tv_usec - last_progress_update.tv_usec);
if (elapsed_us < (1000000 / UX_PROGRESS_REFRESH_RATE))
return;
}
if (value > max)
value = max;
va_start(ap, max);
vsnprintf(task_name, sizeof(task_name), fmt, ap);
va_end(ap);
bar_length = ux_width - (20 + 4 + 6);
percent = (float)value / max;
bars = percent * bar_length;
dashes = bar_length - bars;
printf("%-20.20s [%.*s%.*s] %1.2f%%%n\r", task_name,
bars, progress_hashes,
dashes, progress_dashes,
percent * 100,
&ux_cur_line_length);
fflush(stdout);
gettimeofday(&last_progress_update, NULL);
}