Allow to cancel and to print progress of the current diffing job (#4051)

This commit is contained in:
Giovanni 2023-12-29 14:25:38 +08:00 committed by GitHub
parent b28f19443b
commit ccbfe57b68
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 193 additions and 75 deletions

View file

@ -65,8 +65,10 @@ compute the difference between two files based on the specified type:
symbols | Compares symbols found in the files
.It Fl T
show timestamp information in the output.
.It Fl v
.It Fl V
show version information for rz-diff.
.It Fl v
be more verbose (stderr output).
.Pp
palette colors can be changed by adding the following lines
inside the $HOME/.rizinrc file

View file

@ -33,21 +33,30 @@ typedef struct shared_context_t {
RzThreadLock *lock_b;
RzAnalysis *analysis_a;
RzAnalysis *analysis_b;
RzAtomicBool *loop;
} SharedContext;
typedef struct match_ui_info_t {
SharedContext *shared;
void *user;
RzAnalysisMatchThreadInfoCb callback;
} MatchUIInfo;
static bool shared_context_init(SharedContext *context, RzAnalysis *analysis_a, RzAnalysis *analysis_b, RzList /*<void *>*/ *list_a, RzList /*<void *>*/ *list_b, AllocateBuffer alloc_cb) {
RzThreadLock *lock_a = rz_th_lock_new(true);
RzThreadLock *lock_b = analysis_a == analysis_b ? lock_a : rz_th_lock_new(true);
RzThreadQueue *queue = rz_th_queue_new2(rz_list_clone(list_a));
RzThreadQueue *matches = rz_th_queue_new(RZ_THREAD_QUEUE_UNLIMITED, NULL);
RzThreadQueue *unmatch = rz_th_queue_new(RZ_THREAD_QUEUE_UNLIMITED, NULL);
if (!lock_a || !lock_b || !queue || !matches || !unmatch) {
RzAtomicBool *loop = rz_atomic_bool_new(true);
if (!lock_a || !lock_b || !queue || !matches || !unmatch || !loop) {
rz_th_lock_free(lock_a);
lock_a = NULL;
rz_th_lock_free(lock_b);
rz_th_queue_free(queue);
rz_th_queue_free(matches);
rz_th_queue_free(unmatch);
rz_atomic_bool_free(loop);
return false;
}
context->queue = queue;
@ -59,6 +68,7 @@ static bool shared_context_init(SharedContext *context, RzAnalysis *analysis_a,
context->lock_b = lock_b;
context->analysis_a = analysis_a;
context->analysis_b = analysis_b;
context->loop = loop;
return true;
}
@ -238,7 +248,26 @@ static RZ_OWN RzAnalysisMatchPair *match_pair_new(const void *pair_a, const void
return result;
}
static RZ_OWN RzAnalysisMatchResult *analysis_match_result_new(RZ_NONNULL RzAnalysis *analysis_a, RZ_NONNULL RzAnalysis *analysis_b, RZ_NONNULL RzList /*<void *>*/ *list_a, RZ_NONNULL RzList /*<void *>*/ *list_b, RzThreadFunction thread_cb, AllocateBuffer alloc_cb) {
// this thread does not care about thread-safety since it only prints
// data that will always be available during its lifetime.
static void *match_thread_ui(MatchUIInfo *ui_info) {
SharedContext *shared = ui_info->shared;
RzAnalysisMatchThreadInfoCb callback = ui_info->callback;
void *user = ui_info->user;
do {
size_t n_left = rz_th_queue_size(shared->queue);
size_t n_matches = rz_th_queue_size(shared->matches);
if (!callback(n_left, n_matches, user)) {
rz_atomic_bool_set(shared->loop, false);
rz_list_free(rz_th_queue_pop_all(shared->queue));
break;
}
rz_sys_usleep(100000);
} while (!rz_th_queue_is_empty(shared->queue));
return NULL;
}
static RZ_OWN RzAnalysisMatchResult *analysis_match_result_new(RZ_NONNULL RzAnalysisMatchOpt *opt, RZ_NONNULL RzList /*<void *>*/ *list_a, RZ_NONNULL RzList /*<void *>*/ *list_b, RzThreadFunction thread_cb, AllocateBuffer alloc_cb) {
size_t pool_size = 1;
RzListIter *iter;
RzAnalysisMatchPair *pair = NULL;
@ -246,9 +275,11 @@ static RZ_OWN RzAnalysisMatchResult *analysis_match_result_new(RZ_NONNULL RzAnal
RzList *unmatch_a = rz_list_newf((RzListFree)free);
RzList *unmatch_b = rz_list_clone(list_b);
RzThreadPool *pool = rz_th_pool_new(RZ_THREAD_POOL_ALL_CORES);
RzThread *user_thread = NULL;
SharedContext shared = { 0 };
MatchUIInfo ui_info = { 0 };
if (!unmatch_a || !unmatch_b || !pool || !shared_context_init(&shared, analysis_a, analysis_b, list_a, list_b, alloc_cb)) {
if (!unmatch_a || !unmatch_b || !pool || !shared_context_init(&shared, opt->analysis_a, opt->analysis_b, list_a, list_b, alloc_cb)) {
RZ_LOG_ERROR("analysis_match: cannot initialize search context\n");
goto fail;
}
@ -259,8 +290,28 @@ static RZ_OWN RzAnalysisMatchResult *analysis_match_result_new(RZ_NONNULL RzAnal
rz_th_pool_add_thread(pool, rz_th_new((RzThreadFunction)thread_cb, &shared));
}
if (opt->callback) {
ui_info.shared = &shared;
ui_info.user = opt->user;
ui_info.callback = opt->callback;
user_thread = rz_th_new((RzThreadFunction)match_thread_ui, &ui_info);
if (!user_thread) {
rz_atomic_bool_set(shared.loop, false);
rz_list_free(rz_th_queue_pop_all(shared.queue));
rz_th_pool_wait(pool);
goto fail;
}
}
rz_th_pool_wait(pool);
if (!rz_atomic_bool_get(shared.loop)) {
if (user_thread) {
rz_th_wait(user_thread);
}
goto fail;
}
result = RZ_NEW0(RzAnalysisMatchResult);
if (!result) {
goto fail;
@ -270,12 +321,18 @@ static RZ_OWN RzAnalysisMatchResult *analysis_match_result_new(RZ_NONNULL RzAnal
result->unmatch_a = rz_th_queue_pop_all(shared.unmatch);
result->unmatch_b = unmatch_b;
if (user_thread) {
rz_th_wait(user_thread);
opt->callback(0, rz_list_length(result->matches), opt->user);
}
// there is no need to sort unmatch_b because it is already sorted.
rz_list_foreach (result->matches, iter, pair) {
rz_list_delete_data(unmatch_b, (void *)pair->pair_b);
}
rz_th_pool_free(pool);
rz_th_free(user_thread);
shared_context_fini(&shared);
return result;
@ -284,6 +341,7 @@ fail:
shared_context_fini(&shared);
rz_list_free(unmatch_a);
rz_list_free(unmatch_b);
rz_th_free(user_thread);
return NULL;
}
@ -310,7 +368,7 @@ static void *analysis_match_basic_blocks(SharedContext *shared) {
ut32 size_a = 0, size_b = 0;
ut8 *buf_a = NULL, *buf_b = NULL;
while ((bb_a = rz_th_queue_pop(shared->queue, false))) {
while (rz_atomic_bool_get(shared->loop) && (bb_a = rz_th_queue_pop(shared->queue, false))) {
if (!shared_context_alloc_a(shared, bb_a, &buf_a, &size_a)) {
RZ_LOG_ERROR("analysis_match: cannot allocate buffer for block 0x%08" PFMT64x " (A)\n", bb_a->addr);
rz_th_queue_push(shared->unmatch, bb_a, true);
@ -320,7 +378,9 @@ static void *analysis_match_basic_blocks(SharedContext *shared) {
match = NULL;
max_similarity = 0.0;
rz_list_foreach (shared->list_b, iter, bb_b) {
if (!shared_context_alloc_b(shared, bb_b, &buf_b, &size_b)) {
if (!rz_atomic_bool_get(shared->loop)) {
break;
} else if (!shared_context_alloc_b(shared, bb_b, &buf_b, &size_b)) {
RZ_LOG_ERROR("analysis_match: cannot allocate buffer for block 0x%08" PFMT64x " (B)\n", bb_b->addr);
continue;
}
@ -352,15 +412,15 @@ static void *analysis_match_basic_blocks(SharedContext *shared) {
/**
* \brief Finds matching basic blocks of 2 given functions using the same RzAnalysis core
*
* \param analysis The RzAnalysis struct to use
* \param fcn_a The input function A
* \param fcn_b The input function B
* \param fcn_a The input function A
* \param fcn_b The input function B
* \param opt The RzAnalysisMatchOpt struct to use
*
* \return On success returns a valid pointer to RzAnalysisMatchResult otherwise NULL
*/
RZ_API RZ_OWN RzAnalysisMatchResult *rz_analysis_match_basic_blocks(RZ_NONNULL RzAnalysis *analysis, RZ_NONNULL RzAnalysisFunction *fcn_a, RZ_NONNULL RzAnalysisFunction *fcn_b) {
rz_return_val_if_fail(analysis && fcn_a && fcn_b, NULL);
return analysis_match_result_new(analysis, analysis, fcn_a->bbs, fcn_b->bbs, (RzThreadFunction)analysis_match_basic_blocks, (AllocateBuffer)basic_block_data_new);
RZ_API RZ_OWN RzAnalysisMatchResult *rz_analysis_match_basic_blocks(RZ_NONNULL RzAnalysisFunction *fcn_a, RZ_NONNULL RzAnalysisFunction *fcn_b, RZ_NONNULL RzAnalysisMatchOpt *opt) {
rz_return_val_if_fail(opt && opt->analysis_a && opt->analysis_b && fcn_a && fcn_b, NULL);
return analysis_match_result_new(opt, fcn_a->bbs, fcn_b->bbs, (RzThreadFunction)analysis_match_basic_blocks, (AllocateBuffer)basic_block_data_new);
}
static bool function_name_cmp(RzAnalysisFunction *fcn_a, RzAnalysisFunction *fcn_b) {
@ -382,7 +442,7 @@ static void *analysis_match_functions(SharedContext *shared) {
ut32 size_a = 0, size_b = 0;
ut8 *buf_a = NULL, *buf_b = NULL;
while ((fcn_a = rz_th_queue_pop(shared->queue, false))) {
while (rz_atomic_bool_get(shared->loop) && (fcn_a = rz_th_queue_pop(shared->queue, false))) {
if (!shared_context_alloc_a(shared, fcn_a, &buf_a, &size_a)) {
RZ_LOG_ERROR("analysis_match: cannot allocate buffer for function %s (A)\n", fcn_a->name);
rz_th_queue_push(shared->unmatch, fcn_a, true);
@ -392,7 +452,9 @@ static void *analysis_match_functions(SharedContext *shared) {
match = NULL;
max_similarity = 0.0;
rz_list_foreach (shared->list_b, iter, fcn_b) {
if (!shared_context_alloc_b(shared, fcn_b, &buf_b, &size_b)) {
if (!rz_atomic_bool_get(shared->loop)) {
break;
} else if (!shared_context_alloc_b(shared, fcn_b, &buf_b, &size_b)) {
RZ_LOG_ERROR("analysis_match: cannot allocate buffer for function %s (B)\n", fcn_b->name);
continue;
}
@ -428,43 +490,13 @@ static void *analysis_match_functions(SharedContext *shared) {
/**
* \brief Finds matching functions of 2 given lists of functions using the same RzAnalysis core
*
* \param analysis The RzAnalysis struct to use
* \param fcn_a The input list A of functions
* \param fcn_b The input list B of functions
* \param list_a The input list A of functions
* \param list_b The input list B of functions
* \param opt The RzAnalysisMatchOpt struct to use
*
* \return On success returns a valid pointer to RzAnalysisMatchResult otherwise NULL
*/
RZ_API RZ_OWN RzAnalysisMatchResult *rz_analysis_match_functions(RZ_NONNULL RzAnalysis *analysis, RzList /*<RzAnalysisFunction *>*/ *list_a, RzList /*<RzAnalysisFunction *>*/ *list_b) {
rz_return_val_if_fail(analysis && list_a && list_b, NULL);
return analysis_match_result_new(analysis, analysis, list_a, list_b, (RzThreadFunction)analysis_match_functions, (AllocateBuffer)function_data_new);
}
/**
* \brief Finds matching basic blocks of 2 given functions using two different RzAnalysis cores
*
* \param analysis_a The RzAnalysis struct for fcn_a to use
* \param fcn_a The input function A
* \param analysis_b The RzAnalysis struct for fcn_b to use
* \param fcn_b The input function B
*
* \return On success returns a valid pointer to RzAnalysisMatchResult otherwise NULL
*/
RZ_API RZ_OWN RzAnalysisMatchResult *rz_analysis_match_basic_blocks_2(RZ_NONNULL RzAnalysis *analysis_a, RZ_NONNULL RzAnalysisFunction *fcn_a, RZ_NONNULL RzAnalysis *analysis_b, RZ_NONNULL RzAnalysisFunction *fcn_b) {
rz_return_val_if_fail(analysis_a && analysis_b && fcn_a && fcn_b, NULL);
return analysis_match_result_new(analysis_a, analysis_b, fcn_a->bbs, fcn_b->bbs, (RzThreadFunction)analysis_match_basic_blocks, (AllocateBuffer)basic_block_data_new);
}
/**
* \brief Finds matching functions of 2 given lists of functions using two different RzAnalysis cores
*
* \param analysis_a The RzAnalysis struct for list_a to use
* \param list_a The input list A of functions
* \param analysis_b The RzAnalysis struct for list_b to use
* \param list_b The input list B of functions
*
* \return On success returns a valid pointer to RzAnalysisMatchResult otherwise NULL
*/
RZ_API RZ_OWN RzAnalysisMatchResult *rz_analysis_match_functions_2(RZ_NONNULL RzAnalysis *analysis_a, RzList /*<RzAnalysisFunction *>*/ *list_a, RZ_NONNULL RzAnalysis *analysis_b, RzList /*<RzAnalysisFunction *>*/ *list_b) {
rz_return_val_if_fail(analysis_a && analysis_b && list_a && list_b, NULL);
return analysis_match_result_new(analysis_a, analysis_b, list_a, list_b, (RzThreadFunction)analysis_match_functions, (AllocateBuffer)function_data_new);
RZ_API RZ_OWN RzAnalysisMatchResult *rz_analysis_match_functions(RzList /*<RzAnalysisFunction *>*/ *list_a, RzList /*<RzAnalysisFunction *>*/ *list_b, RZ_NONNULL RzAnalysisMatchOpt *opt) {
rz_return_val_if_fail(opt && opt->analysis_a && opt->analysis_b && list_a && list_b, NULL);
return analysis_match_result_new(opt, list_a, list_b, (RzThreadFunction)analysis_match_functions, (AllocateBuffer)function_data_new);
}

View file

@ -1885,6 +1885,20 @@ typedef enum {
#define RZ_ANALYSIS_SIMILARITY_PARTIAL_STR "PARTIAL"
#define RZ_ANALYSIS_SIMILARITY_UNLIKE_STR "UNLIKE"
typedef struct rz_analysis_match_info_t {
ut32 queue_len; ///< Total number of element left in the queue.
ut32 percentage; ///< Progress made by the search thread.
} RzAnalysisMatchThreadInfo;
typedef bool (*RzAnalysisMatchThreadInfoCb)(const size_t n_left, const size_t n_matches, void *user);
typedef struct rz_analysis_match_options_t {
RZ_NONNULL RzAnalysis *analysis_a; ///< Analysis context for the first input
RZ_NONNULL RzAnalysis *analysis_b; ///< Analysis context for the second input (can be the same as analysis_a)
RzAnalysisMatchThreadInfoCb callback; ///< When set allows to get the thread information
void *user; ///< User pointer to pass to the callback function for the thread info
} RzAnalysisMatchOpt;
typedef struct rz_analysis_match_pair_t {
const void *pair_a; ///< Match pair from input A (the pointers are either RzAnalysisBlock or RzAnalysisFunction)
const void *pair_b; ///< Match pair from input B (the pointers are either RzAnalysisBlock or RzAnalysisFunction)
@ -1909,10 +1923,8 @@ RZ_API double rz_analysis_similarity_basic_block(RZ_NONNULL RzAnalysis *analysis
RZ_API double rz_analysis_similarity_function(RZ_NONNULL RzAnalysis *analysis, RZ_NONNULL RzAnalysisFunction *fcn_a, RZ_NONNULL RzAnalysisFunction *fcn_b);
RZ_API double rz_analysis_similarity_basic_block_2(RZ_NONNULL RzAnalysis *analysis_a, RZ_NONNULL RzAnalysisBlock *bb_a, RZ_NONNULL RzAnalysis *analysis_b, RZ_NONNULL RzAnalysisBlock *bb_b);
RZ_API double rz_analysis_similarity_function_2(RZ_NONNULL RzAnalysis *analysis_a, RZ_NONNULL RzAnalysisFunction *fcn_a, RZ_NONNULL RzAnalysis *analysis_b, RZ_NONNULL RzAnalysisFunction *fcn_b);
RZ_API RZ_OWN RzAnalysisMatchResult *rz_analysis_match_basic_blocks(RZ_NONNULL RzAnalysis *analysis, RZ_NONNULL RzAnalysisFunction *fcn_a, RZ_NONNULL RzAnalysisFunction *fcn_b);
RZ_API RZ_OWN RzAnalysisMatchResult *rz_analysis_match_functions(RZ_NONNULL RzAnalysis *analysis, RzList /*<RzAnalysisFunction *>*/ *list_a, RzList /*<RzAnalysisFunction *>*/ *list_b);
RZ_API RZ_OWN RzAnalysisMatchResult *rz_analysis_match_basic_blocks_2(RZ_NONNULL RzAnalysis *analysis_a, RZ_NONNULL RzAnalysisFunction *fcn_a, RZ_NONNULL RzAnalysis *analysis_b, RZ_NONNULL RzAnalysisFunction *fcn_b);
RZ_API RZ_OWN RzAnalysisMatchResult *rz_analysis_match_functions_2(RZ_NONNULL RzAnalysis *analysis_a, RzList /*<RzAnalysisFunction *>*/ *list_a, RZ_NONNULL RzAnalysis *analysis_b, RzList /*<RzAnalysisFunction *>*/ *list_b);
RZ_API RZ_OWN RzAnalysisMatchResult *rz_analysis_match_basic_blocks(RZ_NONNULL RzAnalysisFunction *fcn_a, RZ_NONNULL RzAnalysisFunction *fcn_b, RZ_NONNULL RzAnalysisMatchOpt *opt);
RZ_API RZ_OWN RzAnalysisMatchResult *rz_analysis_match_functions(RzList /*<RzAnalysisFunction *>*/ *list_a, RzList /*<RzAnalysisFunction *>*/ *list_b, RZ_NONNULL RzAnalysisMatchOpt *opt);
RZ_API void rz_analysis_match_result_free(RZ_NULLABLE RzAnalysisMatchResult *result);
/* value.c */

View file

@ -23,7 +23,7 @@ typedef struct rz_basefind_t {
typedef struct rz_basefind_info_t {
ut32 n_threads; ///< Total number of search threads.
ut32 thread_idx; ///< Sesarch thread number.
ut32 thread_idx; ///< Thread number.
ut64 begin_address; ///< Thread related search address (start).
ut64 current_address; ///< Thread related search address (current).
ut64 end_address; ///< Thread related search address (end).

View file

@ -80,6 +80,7 @@ RZ_API RZ_OWN void *rz_th_queue_wait_pop(RZ_NONNULL RzThreadQueue *queue, bool t
RZ_API RZ_OWN RzList /*<void *>*/ *rz_th_queue_pop_all(RZ_NONNULL RzThreadQueue *queue);
RZ_API bool rz_th_queue_is_empty(RZ_NONNULL RzThreadQueue *queue);
RZ_API bool rz_th_queue_is_full(RZ_NONNULL RzThreadQueue *queue);
RZ_API size_t rz_th_queue_size(RZ_NONNULL RzThreadQueue *queue);
RZ_API RZ_OWN RzAtomicBool *rz_atomic_bool_new(bool value);
RZ_API void rz_atomic_bool_free(RZ_NULLABLE RzAtomicBool *tbool);

View file

@ -78,6 +78,7 @@ typedef struct diff_context_t {
bool colors;
bool analyze_all;
bool command_line;
bool verbose;
const char *architecture;
const char *input_a;
const char *input_b;
@ -211,7 +212,8 @@ static void rz_diff_show_help(bool usage_only) {
" -h show the help message\n"
" -j json output\n"
" -q quite output\n"
" -v show version information\n"
" -V show version information\n"
" -v be more verbose (stderr output)\n"
" -e [k=v] set an evaluable config variable\n"
" -A compare virtual and physical addresses\n"
" -B run 'aaa' when loading the bin\n"
@ -270,7 +272,7 @@ static void rz_diff_parse_arguments(int argc, const char **argv, DiffContext *ct
RzGetopt opt;
int c;
rz_getopt_init(&opt, argc, argv, "hHjqviABCTa:b:e:d:t:0:1:S:");
rz_getopt_init(&opt, argc, argv, "hHjqvViABCTa:b:e:d:t:0:1:S:");
while ((c = rz_getopt_next(&opt)) != -1) {
switch (c) {
case '0': rz_diff_ctx_set_def(ctx, input_a, NULL, opt.arg); break;
@ -287,7 +289,8 @@ static void rz_diff_parse_arguments(int argc, const char **argv, DiffContext *ct
case 'j': rz_diff_ctx_set_mode(ctx, DIFF_MODE_JSON); break;
case 'q': rz_diff_ctx_set_mode(ctx, DIFF_MODE_QUIET); break;
case 't': rz_diff_set_def(type, NULL, opt.arg); break;
case 'v': rz_diff_ctx_set_opt(ctx, DIFF_OPT_VERSION); break;
case 'V': rz_diff_ctx_set_opt(ctx, DIFF_OPT_VERSION); break;
case 'v': rz_diff_ctx_set_def(ctx, verbose, false, true); break;
case 'S': rz_diff_set_def(screen, NULL, opt.arg); break;
case 'H': rz_diff_ctx_set_opt(ctx, DIFF_OPT_HEX_VISUAL); break;
case 'e': rz_diff_ctx_add_evar(ctx, opt.arg); break;
@ -584,13 +587,17 @@ static inline RzBinFile *core_get_file(RzCoreFile *cfile) {
return rz_pvector_at(&cfile->binfiles, 0);
}
static RzCoreFile *rz_diff_load_file_with_core(const char *filename, const char *architecture, ut32 arch_bits, RzList /*<char *>*/ *evars, bool colors) {
static RzCoreFile *rz_diff_load_file_with_core(const char *filename, const char *architecture, ut32 arch_bits, RzList /*<char *>*/ *evars, bool colors, bool verbose) {
RzCore *core = NULL;
RzCoreFile *cfile = NULL;
RzBinFile *bfile = NULL;
RzListIter *it;
char *config;
if (verbose) {
fprintf(stderr, "rz-diff: loading file '%s'\n", filename);
}
core = rz_core_new();
if (!core) {
rz_diff_error("cannot allocate core\n");
@ -1297,13 +1304,18 @@ static RzDiff *rz_diff_fields_new(DiffFile *dfile_a, DiffFile *dfile_b, bool com
/**************************************** commands ***************************************/
static char *execute_command(const char *command, const char *filename, DiffContext *ctx) {
RzCoreFile *cfile = rz_diff_load_file_with_core(filename, ctx->architecture, ctx->arch_bits, ctx->evars, ctx->colors);
RzCoreFile *cfile = rz_diff_load_file_with_core(filename, ctx->architecture, ctx->arch_bits, ctx->evars, ctx->colors, ctx->verbose);
if (!cfile) {
return NULL;
}
if (ctx->analyze_all && !rz_core_analysis_everything(cfile->core, false, NULL)) {
rz_diff_error("cannot analyze binary '%s'\n", ctx->file_a);
if (ctx->analyze_all) {
if (ctx->verbose) {
fprintf(stderr, "rz-diff: analysing file '%s'\n", filename);
}
if (!rz_core_analysis_everything(cfile->core, false, NULL)) {
rz_diff_error("cannot analyze binary '%s'\n", filename);
}
}
char *output = rz_core_cmd_str(cfile->core, command);
@ -1818,17 +1830,32 @@ static void diff_graph_as_json(RzCore *core_a, RzAnalysisFunction *fcn_a, RzCore
pj_end(pj);
}
static RzAnalysisFunction *find_best_matching_function(RzAnalysis *analysis_a, RzAnalysis *analysis_b, RzAnalysisFunction *find) {
static bool diff_progess_status(const size_t n_left, const size_t n_matches, void *user) {
rz_cons_clear_line(true);
fprintf(stderr, "rz-diff: to check %" PFMTSZu " | matches %" PFMTSZu "\r", n_left, n_matches);
return !rz_cons_is_breaked();
}
static bool diff_check_ctrl_c(const size_t n_left, const size_t n_matches, void *user) {
return !rz_cons_is_breaked();
}
static RzAnalysisFunction *find_best_matching_function(RzAnalysis *analysis_a, RzAnalysis *analysis_b, RzAnalysisFunction *find, bool verbose) {
RzAnalysisMatchPair *pair = NULL;
RzAnalysisFunction *match = NULL;
RzAnalysisMatchResult *result = NULL;
RzAnalysisMatchOpt opts = { 0 };
RzList *list_a = rz_list_new();
if (!list_a || !rz_list_append(list_a, find)) {
RZ_LOG_ERROR("rz-diff: cannot allocate and initialize RzList for function search\n");
goto fail;
}
result = rz_analysis_match_functions_2(analysis_a, list_a, analysis_b, analysis_b->fcns);
opts.callback = verbose ? diff_progess_status : diff_check_ctrl_c;
opts.analysis_a = analysis_a;
opts.analysis_b = analysis_b;
result = rz_analysis_match_functions(list_a, analysis_b->fcns, &opts);
if (result && rz_list_length(result->matches) > 0) {
pair = (RzAnalysisMatchPair *)rz_list_first(result->matches);
match = (RzAnalysisFunction *)pair->pair_b;
@ -1856,12 +1883,13 @@ static int comparePairBlocks(const RzAnalysisMatchPair *ma, const RzAnalysisMatc
* Each node that doesn't match 100% with the other function will include
* a unified diff of the assembly of the same basic block.
* */
static void core_show_function_diff(RzCore *core_a, ut64 addr_a, RzCore *core_b, ut64 addr_b, DiffMode mode) {
static void core_show_function_diff(RzCore *core_a, ut64 addr_a, RzCore *core_b, ut64 addr_b, DiffMode mode, bool verbose) {
rz_return_if_fail(core_a && core_b);
PJ *pj = NULL;
RzAnalysisFunction *fcn_b = NULL, *fcn_a = NULL;
RzAnalysisMatchResult *result = NULL;
RzAnalysisMatchOpt opts = { 0 };
// find function
fcn_a = rz_analysis_get_function_at(core_a->analysis, addr_a);
@ -1872,7 +1900,7 @@ static void core_show_function_diff(RzCore *core_a, ut64 addr_a, RzCore *core_b,
if (addr_b == UT64_MAX) {
// find matching function on core B
fcn_b = find_best_matching_function(core_a->analysis, core_b->analysis, fcn_a);
fcn_b = find_best_matching_function(core_a->analysis, core_b->analysis, fcn_a, verbose);
if (!fcn_b) {
RZ_LOG_ERROR("rz-diff: cannot find best matching function for function at 0x%" PFMT64x "\n", addr_a);
return;
@ -1885,8 +1913,12 @@ static void core_show_function_diff(RzCore *core_a, ut64 addr_a, RzCore *core_b,
}
}
opts.callback = verbose ? diff_progess_status : diff_check_ctrl_c;
opts.analysis_a = core_a->analysis;
opts.analysis_b = core_b->analysis;
// calculate all the matches between the basic blocks of the 2 functions.
result = rz_analysis_match_basic_blocks_2(core_a->analysis, fcn_a, core_b->analysis, fcn_b);
result = rz_analysis_match_basic_blocks(fcn_a, fcn_b, &opts);
if (!result) {
RZ_LOG_ERROR("rz-diff: cannot calculate matching basic blocks for function at 0x%" PFMT64x "\n", addr_a);
return;
@ -1998,7 +2030,7 @@ static int comparePairFunctions(const RzAnalysisMatchPair *ma, const RzAnalysisM
* Then the scores are shown in a table (when in quiet mode, the table
* is headerless)
* */
static void core_diff_show(RzCore *core_a, RzCore *core_b, DiffMode mode) {
static void core_diff_show(RzCore *core_a, RzCore *core_b, DiffMode mode, bool verbose) {
rz_return_if_fail(core_a && core_b);
char *output = NULL;
@ -2007,6 +2039,7 @@ static void core_diff_show(RzCore *core_a, RzCore *core_b, DiffMode mode) {
RzTable *table = NULL;
RzAnalysisMatchResult *result = NULL;
RzAnalysisMatchPair *pair = NULL;
RzAnalysisMatchOpt opts = { 0 };
RzAnalysisFunction *fcn_a = NULL, *fcn_b = NULL;
RzListIter *iter = NULL;
bool color = false, no_name = false;
@ -2023,8 +2056,12 @@ static void core_diff_show(RzCore *core_a, RzCore *core_b, DiffMode mode) {
goto fail;
}
opts.callback = verbose ? diff_progess_status : diff_check_ctrl_c;
opts.analysis_a = core_a->analysis;
opts.analysis_b = core_b->analysis;
// calculate all the matches between the functions of the 2 different core files.
result = rz_analysis_match_functions_2(core_a->analysis, fcns_a, core_b->analysis, fcns_b);
result = rz_analysis_match_functions(fcns_a, fcns_b, &opts);
if (!result) {
RZ_LOG_ERROR("rz-diff: cannot perform matching functions search\n");
goto fail;
@ -2145,12 +2182,12 @@ static bool rz_diff_graphs_files(DiffContext *ctx) {
RzCoreFile *a = NULL;
RzCoreFile *b = NULL;
a = rz_diff_load_file_with_core(ctx->file_a, ctx->architecture, ctx->arch_bits, ctx->evars, ctx->colors);
a = rz_diff_load_file_with_core(ctx->file_a, ctx->architecture, ctx->arch_bits, ctx->evars, ctx->colors, ctx->verbose);
if (!a) {
goto rz_diff_graphs_files_bad;
}
b = rz_diff_load_file_with_core(ctx->file_b, ctx->architecture, ctx->arch_bits, ctx->evars, ctx->colors);
b = rz_diff_load_file_with_core(ctx->file_b, ctx->architecture, ctx->arch_bits, ctx->evars, ctx->colors, ctx->verbose);
if (!b) {
goto rz_diff_graphs_files_bad;
}
@ -2170,10 +2207,16 @@ static bool rz_diff_graphs_files(DiffContext *ctx) {
}
if (ctx->analyze_all) {
if (ctx->verbose) {
fprintf(stderr, "rz-diff: analysing file '%s'\n", ctx->file_a);
}
if (!rz_core_analysis_everything(a->core, false, NULL)) {
rz_diff_error("cannot analyze binary '%s'\n", ctx->file_a);
goto rz_diff_graphs_files_bad;
}
if (ctx->verbose) {
fprintf(stderr, "rz-diff: analysing file '%s'\n", ctx->file_b);
}
if (!rz_core_analysis_everything(b->core, false, NULL)) {
rz_diff_error("cannot analyze binary '%s'\n", ctx->file_b);
goto rz_diff_graphs_files_bad;
@ -2189,17 +2232,29 @@ static bool rz_diff_graphs_files(DiffContext *ctx) {
goto rz_diff_graphs_files_bad;
}
}
core_show_function_diff(a->core, address_a, b->core, address_b, ctx->mode);
if (ctx->verbose) {
fprintf(stderr, "rz-diff: start diffing.\n");
}
core_show_function_diff(a->core, address_a, b->core, address_b, ctx->mode, ctx->verbose);
} else {
if (ctx->verbose) {
fprintf(stderr, "rz-diff: analysing file '%s'\n", ctx->file_a);
}
if (!rz_core_analysis_everything(a->core, false, NULL)) {
rz_diff_error("cannot analyze binary '%s'\n", ctx->file_a);
goto rz_diff_graphs_files_bad;
}
if (ctx->verbose) {
fprintf(stderr, "rz-diff: analysing file '%s'\n", ctx->file_b);
}
if (!rz_core_analysis_everything(b->core, false, NULL)) {
rz_diff_error("cannot analyze binary '%s'\n", ctx->file_b);
goto rz_diff_graphs_files_bad;
}
core_diff_show(a->core, b->core, ctx->mode);
if (ctx->verbose) {
fprintf(stderr, "rz-diff: start diffing.\n");
}
core_diff_show(a->core, b->core, ctx->mode, ctx->verbose);
}
success = true;

View file

@ -196,6 +196,22 @@ RZ_API bool rz_th_queue_is_full(RZ_NONNULL RzThreadQueue *queue) {
return is_full;
}
/**
* \brief Returns the total number of element in the queue (thread-safe)
*
* \param queue The RzThreadQueue to use
*
* \return Returns the total number of element in the queue
*/
RZ_API size_t rz_th_queue_size(RZ_NONNULL RzThreadQueue *queue) {
rz_return_val_if_fail(queue, false);
rz_th_lock_enter(queue->lock);
size_t size = rz_list_length(queue->list);
rz_th_lock_leave(queue->lock);
return size;
}
/**
* \brief Removes all elements from the queue, but does not awaits when empty.
*

View file

@ -14,9 +14,9 @@ ERROR: rz-diff: error, cannot open a file without a name.
EOF
RUN
NAME=rz-diff -v~commit?"
NAME=rz-diff -V~commit?"
FILE==
CMDS=!!rz-diff -C -v~commit?
CMDS=!!rz-diff -C -V~commit?
EXPECT=<<EOF
1
EOF