Merge remote-tracking branch

'origin/GP-3195-dragonmacher-parallel-decomp-api-tweak' (Closes #5091)
This commit is contained in:
Ryan Kurtz
2023-03-16 12:07:40 -04:00
2 changed files with 43 additions and 44 deletions

View File

@@ -18,8 +18,7 @@ package ghidra.app.decompiler.parallel;
import java.util.*; import java.util.*;
import java.util.function.Consumer; import java.util.function.Consumer;
import generic.concurrent.QCallback; import generic.concurrent.*;
import generic.concurrent.QResult;
import ghidra.app.util.DecompilerConcurrentQ; import ghidra.app.util.DecompilerConcurrentQ;
import ghidra.program.model.address.AddressSetView; import ghidra.program.model.address.AddressSetView;
import ghidra.program.model.listing.*; import ghidra.program.model.listing.*;
@@ -31,8 +30,8 @@ public class ParallelDecompiler {
/** /**
* Decompile the given functions using multiple decompilers * Decompile the given functions using multiple decompilers
* *
* @param callback the callback to be called for each that is processed * @param callback the callback to be called for each item that is processed
* @param program the program * @param program the program
* @param addresses the addresses restricting which functions to decompile * @param addresses the addresses restricting which functions to decompile
* @param monitor the task monitor * @param monitor the task monitor
@@ -53,8 +52,8 @@ public class ParallelDecompiler {
/** /**
* Decompile the given functions using multiple decompilers * Decompile the given functions using multiple decompilers
* *
* @param callback the callback to be called for each that is processed * @param callback the callback to be called for each item that is processed
* @param functions the functions to decompile * @param functions the functions to decompile
* @param monitor the task monitor * @param monitor the task monitor
* @return the list of client results * @return the list of client results
@@ -62,41 +61,40 @@ public class ParallelDecompiler {
* @throws Exception if any other exception occurs * @throws Exception if any other exception occurs
*/ */
public static <R> List<R> decompileFunctions(QCallback<Function, R> callback, public static <R> List<R> decompileFunctions(QCallback<Function, R> callback,
Collection<Function> functions, Collection<Function> functions, TaskMonitor monitor)
TaskMonitor monitor)
throws InterruptedException, Exception { throws InterruptedException, Exception {
List<R> results = List<R> results =
doDecompileFunctions(callback, functions.iterator(), functions.size(), doDecompileFunctions(callback, functions.iterator(), functions.size(), monitor);
monitor);
return results; return results;
} }
/** /**
* Decompile the given functions using multiple decompilers. * Decompile the given functions using multiple decompilers.
* *
* <p>Results will be passed to the given consumer as they are produced. Calling this * <p>Results will be passed to the given consumer as they are produced. Calling this
* method allows you to handle results as they are discovered. * method allows you to handle results as they are discovered.
* *
* <p><strong>This method will wait for all processing before returning.</strong> * <p><strong>This method will wait for all processing before returning.</strong>
* *
* @param callback the callback to be called for each that is processed * @param callback the callback to be called for each that is processed
* @param program the program * @param program the program
* @param functions the functions to decompile * @param functions the functions to decompile
* @param resultsConsumer the consumer to which results will be passed * @param resultsConsumer the consumer to which results will be passed
* @param monitor the task monitor * @param monitor the task monitor
* @throws InterruptedException if interrupted * @throws InterruptedException if interrupted
* @throws Exception if any other exception occurs * @throws Exception if any other exception occurs
*/ */
public static <R> void decompileFunctions(QCallback<Function, R> callback, public static <R> void decompileFunctions(QCallback<Function, R> callback, Program program,
Program program, Iterator<Function> functions, Consumer<R> resultsConsumer, TaskMonitor monitor)
Iterator<Function> functions, Consumer<R> resultsConsumer,
TaskMonitor monitor)
throws InterruptedException, Exception { throws InterruptedException, Exception {
int max = program.getFunctionManager().getFunctionCount(); int max = program.getFunctionManager().getFunctionCount();
boolean collectResults = false; // the client will process results as they arrive
GThreadPool threadPool = GThreadPool.getSharedThreadPool(THREAD_POOL_NAME);
DecompilerConcurrentQ<Function, R> queue = DecompilerConcurrentQ<Function, R> queue =
new DecompilerConcurrentQ<>(callback, THREAD_POOL_NAME, monitor); new DecompilerConcurrentQ<>(callback, threadPool, collectResults, monitor);
monitor.initialize(max); monitor.initialize(max);
queue.process(functions, resultsConsumer); queue.process(functions, resultsConsumer);
queue.waitUntilDone(); queue.waitUntilDone();
@@ -130,14 +128,14 @@ public class ParallelDecompiler {
} }
/** /**
* Creates an object that can be used to perform decompilation of a limited number of * Creates an object that can be used to perform decompilation of a limited number of
* functions at a time, as opposed to working over an entire range of functions at once. * functions at a time, as opposed to working over an entire range of functions at once.
* {@link #decompileFunctions(QCallback, Program, AddressSetView, TaskMonitor)} will create * {@link #decompileFunctions(QCallback, Program, AddressSetView, TaskMonitor)} will create
* and tear down concurrent data structures on each use, making repeated calls less efficient. * and tear down concurrent data structures on each use, making repeated calls less efficient.
* You would use this method when you wish to perform periodic work as results are returned * You would use this method when you wish to perform periodic work as results are returned
* <b>and when using the callback mechanism is not sufficient</b> such as when ordering of * <b>and when using the callback mechanism is not sufficient</b> such as when ordering of
* results is required. * results is required.
* *
* @param callback the callback required to perform work. * @param callback the callback required to perform work.
* @param monitor the monitor used to report progress and to cancel * @param monitor the monitor used to report progress and to cancel
* @return the parallel decompiler used for decompiling. * @return the parallel decompiler used for decompiling.

View File

@@ -28,14 +28,14 @@ import utility.function.Dummy;
/** /**
* A class to perform some of the boilerplate setup of the {@link ConcurrentQ} that is shared * A class to perform some of the boilerplate setup of the {@link ConcurrentQ} that is shared
* amongst clients that perform decompilation in parallel. * amongst clients that perform decompilation in parallel.
* *
* <p>This class can be used in a blocking or non-blocking fashion. * <p>This class can be used in a blocking or non-blocking fashion.
* *
* <ul> * <ul>
* <li>For blocking usage, call * <li>For blocking usage, call
* one of the <u>{@code add}</u> methods to put items in the queue and then call * one of the <u>{@code add}</u> methods to put items in the queue and then call
* {@link #waitForResults()}.</li> * {@link #waitForResults()}.</li>
* <li>For non-blocking usage, simply call * <li>For non-blocking usage, simply call
* {@link #process(Iterator, Consumer)}, passing the consumer of the results.</li> * {@link #process(Iterator, Consumer)}, passing the consumer of the results.</li>
* </ol> * </ol>
* <p> * <p>
@@ -50,22 +50,23 @@ public class DecompilerConcurrentQ<I, R> {
private Consumer<R> resultConsumer = Dummy.consumer(); private Consumer<R> resultConsumer = Dummy.consumer();
public DecompilerConcurrentQ(QCallback<I, R> callback, TaskMonitor monitor) { public DecompilerConcurrentQ(QCallback<I, R> callback, TaskMonitor monitor) {
this(callback, AutoAnalysisManager.getSharedAnalsysThreadPool(), monitor); this(callback, AutoAnalysisManager.getSharedAnalsysThreadPool(), true, monitor);
} }
public DecompilerConcurrentQ(QCallback<I, R> callback, String threadPoolName, public DecompilerConcurrentQ(QCallback<I, R> callback, String threadPoolName,
TaskMonitor monitor) { TaskMonitor monitor) {
this(callback, GThreadPool.getSharedThreadPool(threadPoolName), monitor); this(callback, GThreadPool.getSharedThreadPool(threadPoolName), true, monitor);
} }
private DecompilerConcurrentQ(QCallback<I, R> callback, GThreadPool pool, TaskMonitor monitor) { public DecompilerConcurrentQ(QCallback<I, R> callback, GThreadPool pool, boolean collectResults,
TaskMonitor monitor) {
// @formatter:off // @formatter:off
queue = new ConcurrentQBuilder<I, R>() queue = new ConcurrentQBuilder<I, R>()
.setCollectResults(true) .setCollectResults(collectResults)
.setThreadPool(pool) .setThreadPool(pool)
.setMonitor(monitor) .setMonitor(monitor)
.setListener(new InternalResultListener()) .setListener(new InternalResultListener())
.build(callback); .build(callback);
// @formatter:on // @formatter:on
} }
@@ -84,7 +85,7 @@ public class DecompilerConcurrentQ<I, R> {
/** /**
* Adds all items to the queue for processing. The results will be passed to the given consumer * Adds all items to the queue for processing. The results will be passed to the given consumer
* as they are produced. * as they are produced.
* *
* @param functions the functions to process * @param functions the functions to process
* @param consumer the results consumer * @param consumer the results consumer
*/ */
@@ -96,7 +97,7 @@ public class DecompilerConcurrentQ<I, R> {
/** /**
* Waits for all results to be delivered. The client is responsible for processing the * Waits for all results to be delivered. The client is responsible for processing the
* results and handling any exceptions that may have occurred. * results and handling any exceptions that may have occurred.
* *
* @return all results * @return all results
* @throws InterruptedException if interrupted while waiting * @throws InterruptedException if interrupted while waiting
*/ */
@@ -110,10 +111,10 @@ public class DecompilerConcurrentQ<I, R> {
} }
/** /**
* Waits for all work to finish. Any exception encountered will trigger all processing to * Waits for all work to finish. Any exception encountered will trigger all processing to
* stop. If you wish for the work to continue despite exceptions, then use * stop. If you wish for the work to continue despite exceptions, then use
* {@link #waitForResults()}. * {@link #waitForResults()}.
* *
* @throws InterruptedException if interrupted while waiting * @throws InterruptedException if interrupted while waiting
* @throws Exception any exception that is encountered while processing items. * @throws Exception any exception that is encountered while processing items.
*/ */
@@ -131,9 +132,9 @@ public class DecompilerConcurrentQ<I, R> {
} }
/** /**
* Calls dispose on the queue being processed. Further, the call will block for up to * Calls dispose on the queue being processed. Further, the call will block for up to
* <tt>timeoutSeconds</tt> while waiting for the queue to finish processing. * <tt>timeoutSeconds</tt> while waiting for the queue to finish processing.
* *
* @param timeoutSeconds the number of seconds to wait for the disposed queue to finish * @param timeoutSeconds the number of seconds to wait for the disposed queue to finish
* processing * processing
*/ */
@@ -164,7 +165,7 @@ public class DecompilerConcurrentQ<I, R> {
} }
} }
catch (Throwable t) { catch (Throwable t) {
// This code is an asynchronous callback. Handle the exception the same way as // This code is an asynchronous callback. Handle the exception the same way as
// the waitXyz() method do, which is to shutdown the queue. // the waitXyz() method do, which is to shutdown the queue.
Msg.error(this, "Unexpected exception getting Decompiler result", t); Msg.error(this, "Unexpected exception getting Decompiler result", t);
queue.dispose(); queue.dispose();