From 48c2c5f28738bdbaff34affbede8bad041dbb258 Mon Sep 17 00:00:00 2001 From: Peiwei Hu Date: Thu, 16 May 2024 15:34:27 +0800 Subject: [PATCH] Add file percentage in visual mode (V/Vv) --- librz/core/core_private.h | 2 + librz/core/tui/visual.c | 125 ++++++++++++++++++++++++++++++++++++++ librz/core/tui/vmenus.c | 27 +++++++- test/db/cmd/cmd_visual | 2 +- 4 files changed, 154 insertions(+), 2 deletions(-) diff --git a/librz/core/core_private.h b/librz/core/core_private.h index cd0e597909..4d51624b6d 100644 --- a/librz/core/core_private.h +++ b/librz/core/core_private.h @@ -363,6 +363,8 @@ typedef struct rz_core_visual_t { int current5format; /* Panels */ RzPanelsRoot *panels_root; + /* file percentage */ + float percentage; } RzCoreVisual; RZ_IPI RZ_OWN RzCoreVisual *rz_core_visual_new(); diff --git a/librz/core/tui/visual.c b/librz/core/tui/visual.c index 3a3991ebe0..59bac047e5 100644 --- a/librz/core/tui/visual.c +++ b/librz/core/tui/visual.c @@ -3503,6 +3503,113 @@ RZ_IPI void rz_core_visual_scrollbar_bottom(RzCore *core) { rz_cons_flush(); } +static bool is_in_symbol_range(ut64 sym_addr, ut64 sym_size, ut64 addr) { + if (addr == sym_addr && sym_size == 0) { + return true; + } + if (sym_size == 0) { + return false; + } + return RZ_BETWEEN(sym_addr, addr, sym_addr + sym_size - 1); +} + +/** + * \brief The percentage of the mapped memory region returned for the given address. + * + * \param core RzCore + * \param addr The given address + * \return percentage of mapped memory region, or -1 if not in mapped memory or error happens + */ +static float get_percentage_of_mapped_region(RzCore *core, ut64 addr) { + rz_return_val_if_fail(core, -1); + + float percentage = -1; + RzBinObject *bin_obj = rz_bin_cur_object(core->bin); + RzPVector *sections = rz_bin_object_get_sections(bin_obj); + if (!sections) { + return -1; + } + + RzBinSection *section; + void **iter; + rz_pvector_foreach (sections, iter) { + section = *iter; + if (is_in_symbol_range(section->vaddr, section->vsize, addr)) { + // calculate the percentage + percentage = (addr - section->vaddr) / (float)section->vsize; + break; + } + } + + rz_pvector_free(sections); + return percentage; +} + +/** + * \brief Get the address of the data of the row at the bottom of the screen + * for calculating file percentage + * + * \param core RzCore + * \return captured address + */ +static RZ_OWN char *screen_bottom_address(RzCore *core) { + rz_return_val_if_fail(core, NULL); + + char *rtn = NULL; + // get the line at the bottom of the screen + if (!core->cons->lastline) { + return NULL; + } + char *output = strdup(core->cons->lastline); + size_t line_count = 0, *line_index = rz_str_split_lines(output, &line_count); + int rows; + rz_cons_get_size(&rows); + if (!line_index || rows > line_count) { + goto exit1; + } + char *lastline = output + line_index[rows - 1]; + + // capture the address from the line at the bottom + char *regex_str = ((RzCoreVisual *)core->visual)->printidx == RZ_CORE_VISUAL_MODE_CD ? "[0-9abcdefABCDEF]+" : "0x[0-9ABCDEFabcdef]+"; + RzRegex *re = rz_regex_new(regex_str, RZ_REGEX_EXTENDED, 0); + RzPVector *matches = rz_regex_match_all_not_grouped(re, lastline, RZ_REGEX_ZERO_TERMINATED, 0, RZ_REGEX_DEFAULT); + if (!matches || rz_pvector_empty(matches)) { + goto exit; + } + + RzRegexMatch *match = rz_pvector_at(matches, 0); + rtn = rz_str_ndup(lastline + match->start, match->len); + + // filter address in command, xref like ; CALL XREF from entry.fini0 @ 0x6b67 + char *comment_signs[] = { "@", ";" }; + char *addr_pos = strstr(lastline, rtn); + for (ut32 i = 0; i < sizeof(comment_signs) / sizeof(comment_signs[0]); i++) { + const char *sign_pos = rz_str_strchr(lastline, comment_signs[i]); + if (sign_pos) { + if (addr_pos && addr_pos > sign_pos) { + rtn = NULL; + } + } + } + + // in case the address doesn't have 0x prefix (RZ_CORE_VISUAL_MODE_CD) + if (rtn && !rz_str_startswith_icase(rtn, "0x")) { + ut32 addr_len = strlen(rtn), prefix_len = strlen("0x"); + rtn = realloc(rtn, prefix_len + addr_len + 1); + memmove(rtn + 2, rtn, addr_len); + rtn[0] = '0'; + rtn[1] = 'x'; + } + +exit: + rz_pvector_free(matches); + rz_regex_free(re); +exit1: + free(line_index); + free(output); + return rtn; +} + static void visual_refresh(RzCore *core) { static ut64 oseek = UT64_MAX; const char *vi, *vcmd, *cmd_str; @@ -3606,6 +3713,14 @@ static void visual_refresh(RzCore *core) { } #endif + // get the address in the line at the bottom of the screen to calculate the percentage + char *bottom_addr = screen_bottom_address(core); + if (bottom_addr) { + ut64 addr = rz_num_math(NULL, bottom_addr); + visual->percentage = get_percentage_of_mapped_region(core, addr); + free(bottom_addr); + } + /* this is why there's flickering */ if (core->print->vflush) { rz_cons_visual_flush(); @@ -3624,6 +3739,15 @@ static void visual_refresh(RzCore *core) { if (rz_config_get_i(core->config, "scr.scrollbar")) { rz_core_visual_scrollbar(core); } + + int h, cols = rz_cons_get_size(&h); + if (visual->percentage >= 0) { + char *percentage_str = rz_str_newf("%.1f%%", visual->percentage * 100); + rz_cons_gotoxy(cols - strlen(percentage_str) - 1, h); + rz_cons_printf("%s", percentage_str); + rz_cons_flush(); + free(percentage_str); + } } static void visual_refresh_oneshot(RzCore *core) { @@ -3831,6 +3955,7 @@ RZ_IPI int rz_core_visual(RzCore *core, const char *input) { rz_cons_enable_mouse(false); if (visual->color) { + rz_cons_strcat(Color_RESET); } rz_config_set_i(core->config, "scr.color", visual->color); diff --git a/librz/core/tui/vmenus.c b/librz/core/tui/vmenus.c index 14d0af1bd4..5870698d7a 100644 --- a/librz/core/tui/vmenus.c +++ b/librz/core/tui/vmenus.c @@ -251,6 +251,7 @@ static ut64 var_variables_show(RzCore *core, int idx, int *vindex, int show, int static int level = 0; static st64 delta = 0; +static ut64 column_nlines = 0; // output is used to store the result of printCmds // and avoid duplicated analysis while j and k is pressed static char *output = NULL; @@ -289,7 +290,16 @@ static void rz_core_visual_analysis_refresh_column(RzCore *core, int colpos) { free(cmdf); } if (output) { - // 'h - 2' because we have two new lines in rz_cons_printf + // count the lines of output for calculating percentage + column_nlines = 0; + char *s = output; + while (*s) { + if (*s == '\n') { + column_nlines++; + } + s++; + } + // crop and print output, 'h - 2' because we have two new lines in rz_cons_printf char *out; if (printMode == 1) { out = rz_str_ansi_crop(output, 0, 0, w - colpos, h - 2); @@ -453,6 +463,19 @@ static ut64 rz_core_visual_analysis_refresh(RzCore *core) { rz_warn_if_reached(); break; } + + // print percentage at right corner + cols = rz_cons_get_size(&h); + float p = (float)(delta + h - 2) / (float)column_nlines; + if (p > 1) { + p = 1; + } + char *percentage = rz_str_newf("%.1f%%", p * 100); + // move to the right corner + rz_cons_gotoxy(cols - strlen(percentage) - 1, h); + rz_cons_printf("%s", percentage); + free(percentage); + rz_cons_show_cursor(false); rz_cons_flush(); return addr; } @@ -708,11 +731,13 @@ RZ_IPI void rz_core_visual_analysis(RzCore *core, const char *input) { } break; case ':': { + rz_cons_show_cursor(true); ut64 orig = core->offset; rz_core_seek(core, addr, false); while (rz_core_visual_prompt(core)) ; rz_core_seek(core, orig, false); + rz_cons_show_cursor(false); } continue; case '/': diff --git a/test/db/cmd/cmd_visual b/test/db/cmd/cmd_visual index de994c3fc1..cd1975b396 100644 --- a/test/db/cmd/cmd_visual +++ b/test/db/cmd/cmd_visual @@ -100,7 +100,7 @@ EXPECT=<