Allow to cancel and to print progress of the current diffing job (#4051)
This commit is contained in:
parent
b28f19443b
commit
ccbfe57b68
8 changed files with 193 additions and 75 deletions
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
|
|
@ -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).
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
*
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Reference in a new issue