summaryrefslogtreecommitdiff
path: root/plugins
diff options
context:
space:
mode:
authorMason Reed <mason@vector35.com>2026-03-03 07:57:33 -0800
committerMason Reed <35282038+emesare@users.noreply.github.com>2026-03-24 18:46:48 -0700
commit2c358e705bbde855b12780be86ef19501a62dfed (patch)
tree4f3012bdf36c9a0f7de4218790e6e1aa389621ba /plugins
parent7877f7caa3535a7c477c911ddeafdf5bb14ebc14 (diff)
[WARP] Server-side constraint matching
Reduce networked functions by constraining on the returned set of functions on the server
Diffstat (limited to 'plugins')
-rw-r--r--plugins/warp/api/python/warp.py10
-rw-r--r--plugins/warp/api/warp.cpp9
-rw-r--r--plugins/warp/api/warp.h2
-rw-r--r--plugins/warp/api/warpcore.h2
-rw-r--r--plugins/warp/src/container.rs5
-rw-r--r--plugins/warp/src/container/network.rs36
-rw-r--r--plugins/warp/src/container/network/client.rs25
-rw-r--r--plugins/warp/src/plugin/ffi/container.rs8
-rw-r--r--plugins/warp/src/plugin/settings.rs6
-rw-r--r--plugins/warp/src/plugin/workflow.rs25
-rw-r--r--plugins/warp/ui/shared/fetchdialog.cpp34
-rw-r--r--plugins/warp/ui/shared/fetchdialog.h3
-rw-r--r--plugins/warp/ui/shared/fetcher.cpp28
-rw-r--r--plugins/warp/ui/shared/misc.h4
14 files changed, 129 insertions, 68 deletions
diff --git a/plugins/warp/api/python/warp.py b/plugins/warp/api/python/warp.py
index a699ebbc..2ba1c681 100644
--- a/plugins/warp/api/python/warp.py
+++ b/plugins/warp/api/python/warp.py
@@ -426,11 +426,17 @@ class WarpContainer(metaclass=_WarpContainerMetaclass):
core_guids[i] = guids[i].uuid
return warpcore.BNWARPContainerRemoveTypes(self.handle, source.uuid, core_guids, count)
- def fetch_functions(self, target: WarpTarget, guids: List[FunctionGUID], source_tags: Optional[List[str]] = None):
+ def fetch_functions(self, target: WarpTarget, guids: List[FunctionGUID], source_tags: Optional[List[str]] = None, constraints: Optional[List[ConstraintGUID]] = None):
count = len(guids)
core_guids = (warpcore.BNWARPFunctionGUID * count)()
for i in range(count):
core_guids[i] = guids[i].uuid
+ if constraints is None:
+ constraints = []
+ constraints_count = len(constraints)
+ core_constraints = (warpcore.BNWARPConstraintGUID * constraints_count)()
+ for i in range(constraints_count):
+ core_constraints[i] = constraints[i].uuid
if source_tags is None:
source_tags = []
source_tags_ptr = (ctypes.c_char_p * len(source_tags))()
@@ -438,7 +444,7 @@ class WarpContainer(metaclass=_WarpContainerMetaclass):
for i in range(len(source_tags)):
source_tags_ptr[i] = source_tags[i].encode('utf-8')
source_tags_array_ptr = ctypes.cast(source_tags_ptr, ctypes.POINTER(ctypes.c_char_p))
- warpcore.BNWARPContainerFetchFunctions(self.handle, target.handle, source_tags_array_ptr, source_tags_len, core_guids, count)
+ warpcore.BNWARPContainerFetchFunctions(self.handle, target.handle, source_tags_array_ptr, source_tags_len, core_guids, count, core_constraints, constraints_count)
def get_sources_with_function_guid(self, target: WarpTarget, guid: FunctionGUID) -> List[Source]:
count = ctypes.c_size_t()
diff --git a/plugins/warp/api/warp.cpp b/plugins/warp/api/warp.cpp
index c051e3c7..debbbc00 100644
--- a/plugins/warp/api/warp.cpp
+++ b/plugins/warp/api/warp.cpp
@@ -353,7 +353,7 @@ bool Container::RemoveTypes(const Source &source, const std::vector<TypeGUID> &g
return result;
}
-void Container::FetchFunctions(const Target &target, const std::vector<FunctionGUID> &guids, const std::vector<SourceTag> &tags) const
+void Container::FetchFunctions(const Target &target, const std::vector<FunctionGUID> &guids, const std::vector<SourceTag> &tags, const std::vector<ConstraintGUID> &constraints) const
{
size_t count = guids.size();
BNWARPFunctionGUID *apiGuids = new BNWARPFunctionGUID[count];
@@ -363,9 +363,14 @@ void Container::FetchFunctions(const Target &target, const std::vector<FunctionG
const char** rawTags = new const char*[tagCount];
for (size_t i = 0; i < tagCount; i++)
rawTags[i] = tags[i].c_str();
- BNWARPContainerFetchFunctions(m_object, target.m_object, rawTags, tagCount, apiGuids, count);
+ size_t constraintCount = constraints.size();
+ BNWARPConstraintGUID *apiConstraints = new BNWARPConstraintGUID[constraintCount];
+ for (size_t i = 0; i < constraintCount; i++)
+ apiConstraints[i] = *constraints[i].Raw();
+ BNWARPContainerFetchFunctions(m_object, target.m_object, rawTags, tagCount, apiGuids, count, apiConstraints, constraintCount);
delete[] apiGuids;
delete[] rawTags;
+ delete[] apiConstraints;
}
std::vector<Source> Container::GetSourcesWithFunctionGUID(const Target& target, const FunctionGUID &guid) const
diff --git a/plugins/warp/api/warp.h b/plugins/warp/api/warp.h
index 01f4aaee..ccb4da97 100644
--- a/plugins/warp/api/warp.h
+++ b/plugins/warp/api/warp.h
@@ -409,7 +409,7 @@ namespace Warp {
bool RemoveTypes(const Source &source, const std::vector<TypeGUID> &guids) const;
- void FetchFunctions(const Target &target, const std::vector<FunctionGUID> &guids, const std::vector<SourceTag> &tags = {}) const;
+ void FetchFunctions(const Target &target, const std::vector<FunctionGUID> &guids, const std::vector<SourceTag> &tags = {}, const std::vector<ConstraintGUID> &constraints = {}) const;
std::vector<Source> GetSourcesWithFunctionGUID(const Target &target, const FunctionGUID &guid) const;
diff --git a/plugins/warp/api/warpcore.h b/plugins/warp/api/warpcore.h
index c52485aa..21ef105b 100644
--- a/plugins/warp/api/warpcore.h
+++ b/plugins/warp/api/warpcore.h
@@ -128,7 +128,7 @@ extern "C"
WARP_FFI_API bool BNWARPContainerRemoveFunctions(BNWARPContainer* container, const BNWARPTarget* target, const BNWARPSource* source, BNWARPFunction** functions, size_t count);
WARP_FFI_API bool BNWARPContainerRemoveTypes(BNWARPContainer* container, const BNWARPSource* source, BNWARPTypeGUID* types, size_t count);
- WARP_FFI_API void BNWARPContainerFetchFunctions(BNWARPContainer* container, BNWARPTarget* target, const char** sourceTags, size_t sourceTagCount, const BNWARPTypeGUID* guids, size_t count);
+ WARP_FFI_API void BNWARPContainerFetchFunctions(BNWARPContainer* container, BNWARPTarget* target, const char** sourceTags, size_t sourceTagCount, const BNWARPFunctionGUID* guids, size_t count, const BNWARPConstraintGUID* constraints, size_t constraintCount);
WARP_FFI_API BNWARPSource* BNWARPContainerGetSourcesWithFunctionGUID(BNWARPContainer* container, const BNWARPTarget* target, const BNWARPFunctionGUID* guid, size_t* count);
WARP_FFI_API BNWARPSource* BNWARPContainerGetSourcesWithTypeGUID(BNWARPContainer* container, const BNWARPTypeGUID* guid, size_t* count);
diff --git a/plugins/warp/src/container.rs b/plugins/warp/src/container.rs
index 79c243b3..4feed24c 100644
--- a/plugins/warp/src/container.rs
+++ b/plugins/warp/src/container.rs
@@ -9,6 +9,7 @@ use thiserror::Error;
use uuid::Uuid;
use warp::r#type::guid::TypeGUID;
use warp::r#type::{ComputedType, Type};
+use warp::signature::constraint::ConstraintGUID;
use warp::signature::function::{Function, FunctionGUID};
use warp::symbol::Symbol;
use warp::target::Target;
@@ -295,11 +296,15 @@ pub trait Container: Send + Sync + Display + Debug {
/// Typically, a container that resides only in memory has nothing to fetch, so the default implementation
/// will do nothing. This function is blocking, so assume it will take a few seconds for a container
/// that intends to fetch over the network.
+ ///
+ /// To constrain on the fetched functions, pass a list of [`ConstraintGUID`]s that will be
+ /// used to filter the fetched functions which do not contain at least one of the constraints.
fn fetch_functions(
&self,
_target: &Target,
_tags: &[SourceTag],
_functions: &[FunctionGUID],
+ _constraints: &[ConstraintGUID],
) -> ContainerResult<()> {
Ok(())
}
diff --git a/plugins/warp/src/container/network.rs b/plugins/warp/src/container/network.rs
index 307b44fa..88192844 100644
--- a/plugins/warp/src/container/network.rs
+++ b/plugins/warp/src/container/network.rs
@@ -14,6 +14,7 @@ use warp::r#type::chunk::TypeChunk;
use warp::r#type::guid::TypeGUID;
use warp::r#type::{ComputedType, Type};
use warp::signature::chunk::SignatureChunk;
+use warp::signature::constraint::ConstraintGUID;
use warp::signature::function::{Function, FunctionGUID};
use warp::target::Target;
use warp::{WarpFile, WarpFileHeader};
@@ -45,7 +46,7 @@ pub struct NetworkContainer {
/// NOTE: This is a [`DashMap`] purely for the sake of interior mutability as we do not wish to hold
/// a write lock on the entire container while performing network operations.
known_function_sources: DashMap<FunctionGUID, Vec<SourceId>>,
- /// Populated when user adds function, this is used for writing back to the server.
+ /// Populated when the user adds a function, this is used for writing back to the server.
added_chunks: HashMap<SourceId, Vec<Chunk<'static>>>,
/// Populated when connecting to the server, this is used to determine which sources are writable.
///
@@ -165,18 +166,25 @@ impl NetworkContainer {
/// Every request we store the returned objects on disk, this means that users will first
/// query against the disk objects, then the server. This also means we need to cache functions f
/// or which we have not received any functions for, as otherwise we would keep trying to query it.
- pub fn pull_functions(&self, target: &Target, source: &SourceId, functions: &[FunctionGUID]) {
+ pub fn pull_functions(
+ &self,
+ target: &Target,
+ source: &SourceId,
+ functions: &[FunctionGUID],
+ constraints: &[ConstraintGUID],
+ ) {
let target_id = self.get_target_id(target);
- let file = match self
- .client
- .query_functions(target_id, Some(*source), functions)
- {
- Ok(file) => file,
- Err(e) => {
- tracing::error!("Failed to query functions: {}", e);
- return;
- }
- };
+ let file =
+ match self
+ .client
+ .query_functions(target_id, Some(*source), functions, constraints)
+ {
+ Ok(file) => file,
+ Err(e) => {
+ tracing::error!("Failed to query functions: {}", e);
+ return;
+ }
+ };
tracing::debug!("Got {} chunks from server", file.chunks.len());
for chunk in &file.chunks {
@@ -396,16 +404,18 @@ impl Container for NetworkContainer {
target: &Target,
tags: &[SourceTag],
functions: &[FunctionGUID],
+ constraints: &[ConstraintGUID],
) -> ContainerResult<()> {
// NOTE: Blocking request to get the mapped function sources.
let mapped_unseen_functions =
self.get_unseen_functions_source(Some(&target), tags, functions);
+ // TODO: It would be nice to have a way to not have to pull through each source individually.
// Actually get the function data for the unseen guids, we really only want to do this once per
// session, anymore, and this is annoying!
for (source, unseen_guids) in mapped_unseen_functions {
// NOTE: Blocking request to get the function data in the container cache.
- self.pull_functions(&target, &source, &unseen_guids);
+ self.pull_functions(&target, &source, &unseen_guids, constraints);
}
Ok(())
diff --git a/plugins/warp/src/container/network/client.rs b/plugins/warp/src/container/network/client.rs
index 0b772dcc..3d0af057 100644
--- a/plugins/warp/src/container/network/client.rs
+++ b/plugins/warp/src/container/network/client.rs
@@ -7,12 +7,13 @@ use base64::Engine;
use binaryninja::download::DownloadProvider;
use serde::Deserialize;
use serde_json::json;
-use std::collections::HashMap;
+use std::collections::{HashMap, HashSet};
use std::str::FromStr;
use uuid::Uuid;
use warp::chunk::ChunkKind;
use warp::r#type::guid::TypeGUID;
use warp::r#type::{ComputedType, Type};
+use warp::signature::constraint::ConstraintGUID;
use warp::signature::function::{Function, FunctionGUID};
use warp::target::Target;
use warp::WarpFile;
@@ -30,7 +31,8 @@ pub struct NetworkClient {
impl NetworkClient {
pub fn new(server_url: String, server_token: Option<String>) -> Self {
// TODO: This might want to be kept for the request header?
- let mut headers: Vec<(String, String)> = vec![];
+ let mut headers: Vec<(String, String)> =
+ vec![("Content-Encoding".to_string(), "gzip".to_string())];
if let Some(token) = &server_token {
headers.push(("authorization".to_string(), format!("Bearer {}", token)));
}
@@ -214,13 +216,14 @@ impl NetworkClient {
source: Option<SourceId>,
source_tags: &[SourceTag],
guids: &[FunctionGUID],
+ constraints: &[ConstraintGUID],
) -> serde_json::Value {
- let guids_str: Vec<String> = guids.iter().map(|g| g.to_string()).collect();
+ let guids_str: HashSet<String> = guids.iter().map(|g| g.to_string()).collect();
// TODO: The limit here needs to be somewhat flexible. But 1000 will do for now.
let mut body = json!({
"format": "flatbuffer",
"guids": guids_str,
- "limit": 1000
+ "limit": 10000,
});
if let Some(target_id) = target {
body["target_id"] = json!(target_id);
@@ -231,6 +234,11 @@ impl NetworkClient {
if !source_tags.is_empty() {
body["source_tags"] = json!(source_tags);
}
+ if !constraints.is_empty() {
+ let constraint_guids_str: HashSet<String> =
+ constraints.iter().map(|g| g.to_string()).collect();
+ body["constraints"] = json!(constraint_guids_str);
+ }
body
}
@@ -244,13 +252,13 @@ impl NetworkClient {
target: Option<NetworkTargetId>,
source: Option<SourceId>,
guids: &[FunctionGUID],
+ constraints: &[ConstraintGUID],
) -> Result<WarpFile<'static>, String> {
let query_functions_url = format!("{}/api/v1/functions/query", self.server_url);
// TODO: Allow for source tags? We really only need this in query_functions_source as that
// TODO: is what prevents a undesired source from being "known" to the container.
- let payload = Self::query_functions_body(target, source, &[], guids);
+ let payload = Self::query_functions_body(target, source, &[], guids, constraints);
let mut inst = self.provider.create_instance().unwrap();
-
let resp = inst.post_json(&query_functions_url, self.headers.clone(), &payload)?;
if !resp.is_success() {
return Err(format!(
@@ -275,7 +283,10 @@ impl NetworkClient {
) -> Result<HashMap<SourceId, Vec<FunctionGUID>>, String> {
let query_functions_source_url =
format!("{}/api/v1/functions/query/source", self.server_url);
- let payload = Self::query_functions_body(target, None, tags, guids);
+ // NOTE: We do not filter by constraint guids here since this pass is only responsible for
+ // returning the source ids, not the actual function data, see [`NetworkClient::query_functions`]
+ // for the place where the constraints are applied, and _do_ matter.
+ let payload = Self::query_functions_body(target, None, tags, guids, &[]);
let mut inst = self.provider.create_instance().unwrap();
let resp = inst.post_json(&query_functions_source_url, self.headers.clone(), &payload)?;
diff --git a/plugins/warp/src/plugin/ffi/container.rs b/plugins/warp/src/plugin/ffi/container.rs
index 79f45dcb..aa0c37d3 100644
--- a/plugins/warp/src/plugin/ffi/container.rs
+++ b/plugins/warp/src/plugin/ffi/container.rs
@@ -5,7 +5,8 @@ use crate::container::{
};
use crate::convert::{from_bn_type, to_bn_type};
use crate::plugin::ffi::{
- BNWARPContainer, BNWARPFunction, BNWARPFunctionGUID, BNWARPSource, BNWARPTarget, BNWARPTypeGUID,
+ BNWARPConstraintGUID, BNWARPContainer, BNWARPFunction, BNWARPFunctionGUID, BNWARPSource,
+ BNWARPTarget, BNWARPTypeGUID,
};
use binaryninja::architecture::CoreArchitecture;
use binaryninja::binary_view::BinaryView;
@@ -218,6 +219,8 @@ pub unsafe extern "C" fn BNWARPContainerFetchFunctions(
source_tags_count: usize,
guids: *const BNWARPFunctionGUID,
count: usize,
+ constraints: *const BNWARPConstraintGUID,
+ constraints_count: usize,
) {
let arc_container = ManuallyDrop::new(Arc::from_raw(container));
let Ok(container) = arc_container.read() else {
@@ -234,8 +237,9 @@ pub unsafe extern "C" fn BNWARPContainerFetchFunctions(
.collect();
let guids = unsafe { std::slice::from_raw_parts(guids, count) };
+ let constraints = unsafe { std::slice::from_raw_parts(constraints, constraints_count) };
- if let Err(e) = container.fetch_functions(&target, &source_tags, guids) {
+ if let Err(e) = container.fetch_functions(&target, &source_tags, guids, constraints) {
tracing::error!("Failed to fetch functions: {}", e);
}
}
diff --git a/plugins/warp/src/plugin/settings.rs b/plugins/warp/src/plugin/settings.rs
index 6469be0f..896bee7f 100644
--- a/plugins/warp/src/plugin/settings.rs
+++ b/plugins/warp/src/plugin/settings.rs
@@ -40,7 +40,7 @@ pub struct PluginSettings {
impl PluginSettings {
pub const ALLOWED_SOURCE_TAGS_DEFAULT: [&'static str; 2] = ["official", "trusted"];
pub const ALLOWED_SOURCE_TAGS_SETTING: &'static str = "warp.fetcher.allowedSourceTags";
- pub const FETCH_BATCH_SIZE_DEFAULT: usize = 100;
+ pub const FETCH_BATCH_SIZE_DEFAULT: usize = 10000;
pub const FETCH_BATCH_SIZE_SETTING: &'static str = "warp.fetcher.fetchBatchSize";
pub const LOAD_BUNDLED_FILES_DEFAULT: bool = true;
pub const LOAD_BUNDLED_FILES_SETTING: &'static str = "warp.container.loadBundledFiles";
@@ -81,8 +81,8 @@ impl PluginSettings {
let fetch_size_props = json!({
"title" : "Fetch Batch Limit",
"type" : "number",
- "minValue" : 1,
- "maxValue" : 1000,
+ "minValue" : 100,
+ "maxValue" : 20000,
"default" : Self::FETCH_BATCH_SIZE_DEFAULT,
"description" : "The maximum number of functions to fetch in a single batch. This is used to limit the amount of functions to fetch at once, lowering this value will make the fetch process more comprehensive at the cost of more network requests.",
"ignore" : [],
diff --git a/plugins/warp/src/plugin/workflow.rs b/plugins/warp/src/plugin/workflow.rs
index 1f4ef101..52c8e249 100644
--- a/plugins/warp/src/plugin/workflow.rs
+++ b/plugins/warp/src/plugin/workflow.rs
@@ -24,6 +24,7 @@ use std::cmp::Ordering;
use std::collections::HashMap;
use std::time::Instant;
use warp::r#type::class::function::{Location, RegisterLocation, StackLocation};
+use warp::signature::constraint::ConstraintGUID;
use warp::signature::function::{Function, FunctionGUID};
use warp::target::Target;
@@ -171,7 +172,7 @@ pub fn run_matcher(view: &BinaryView) {
.maximum_possible_functions
.is_some_and(|max| max < matched_functions.len() as u64)
{
- tracing::warn!(
+ tracing::debug!(
"Skipping {}, too many possible functions: {}",
guid,
matched_functions.len()
@@ -270,6 +271,20 @@ pub fn run_fetcher(view: &BinaryView) {
let mut query_opts = QueryOptions::new_with_view(view);
let plugin_settings = PluginSettings::from_settings(&view_settings, &mut query_opts);
+ let is_ignored_func = |f: &BNFunction| !f.function_tags(None, Some(IGNORE_TAG_NAME)).is_empty();
+
+ let constraints: Vec<ConstraintGUID> = view
+ .functions()
+ .iter()
+ // Skip functions that have the ignored tag! Otherwise, we will store their constraints.
+ .filter(|f| !is_ignored_func(f))
+ .filter_map(|f| {
+ let function = try_cached_function_match(&f)?;
+ Some(function.constraints.into_iter().map(|c| c.guid))
+ })
+ .flatten()
+ .collect();
+
let Some(function_set) = FunctionSet::from_view(view) else {
background_task.finish();
return;
@@ -285,8 +300,12 @@ pub fn run_fetcher(view: &BinaryView) {
if background_task.is_cancelled() {
break;
}
- let _ =
- container.fetch_functions(target, &plugin_settings.allowed_source_tags, batch);
+ let _ = container.fetch_functions(
+ target,
+ &plugin_settings.allowed_source_tags,
+ batch,
+ &constraints,
+ );
}
}
});
diff --git a/plugins/warp/ui/shared/fetchdialog.cpp b/plugins/warp/ui/shared/fetchdialog.cpp
index 328f00c2..44d7c237 100644
--- a/plugins/warp/ui/shared/fetchdialog.cpp
+++ b/plugins/warp/ui/shared/fetchdialog.cpp
@@ -67,12 +67,6 @@ WarpFetchDialog::WarpFetchDialog(BinaryViewRef bv, std::shared_ptr<WarpFetcher>
for (const auto& t : GetAllowedTagsFromView(m_bv))
AddListItem(m_tagsList, QString::fromStdString(t));
- // Batch size and matcher checkbox
- m_batchSize = new QSpinBox(this);
- m_batchSize->setRange(10, 1000);
- m_batchSize->setValue(GetBatchSizeFromView(m_bv));
- m_batchSize->setToolTip("Number of functions to fetch in each batch");
-
m_rerunMatcher = new QCheckBox("Re-run matcher after fetch", this);
m_rerunMatcher->setChecked(true);
@@ -83,7 +77,6 @@ WarpFetchDialog::WarpFetchDialog(BinaryViewRef bv, std::shared_ptr<WarpFetcher>
form->addRow(new QLabel("Container: "), m_containerCombo);
form->addRow(new QLabel("Allowed Tags: "), tagWrapper);
- form->addRow(new QLabel("Batch Size: "), m_batchSize);
form->addRow(m_rerunMatcher);
form->addRow(m_clearProcessed);
@@ -144,7 +137,6 @@ void WarpFetchDialog::onAccept()
if (idx > 0) // 0 == All Containers
containerIndex = static_cast<size_t>(idx - 1);
- const auto batch = static_cast<size_t>(m_batchSize->value());
const bool rerun = m_rerunMatcher->isChecked();
const auto tags = collectTags();
@@ -155,7 +147,7 @@ void WarpFetchDialog::onAccept()
m_fetchProcessor->ClearProcessed();
// Execute the network fetch in batches
- runBatchedFetch(containerIndex, tags, batch, rerun);
+ runBatchedFetch(containerIndex, tags, rerun);
accept();
}
@@ -169,7 +161,7 @@ void WarpFetchDialog::onReject()
}
void WarpFetchDialog::runBatchedFetch(const std::optional<size_t>& containerIndex,
- const std::vector<Warp::SourceTag>& allowedTags, size_t batchSize, bool rerunMatcher)
+ const std::vector<Warp::SourceTag>& allowedTags, bool rerunMatcher)
{
if (!m_bv)
return;
@@ -177,42 +169,34 @@ void WarpFetchDialog::runBatchedFetch(const std::optional<size_t>& containerInde
std::vector<Ref<Function>> funcs = m_bv->GetAnalysisFunctionList();
if (funcs.empty())
return;
- const size_t totalFuncs = funcs.size();
- const size_t totalBatches = (totalFuncs + batchSize - 1) / batchSize;
// Create a background task to show progress in the UI
Ref<BackgroundTask> task =
- new BackgroundTask("Fetching WARP functions (0 / " + std::to_string(totalBatches) + ")", false);
+ new BackgroundTask("Fetching WARP functions (0 / " + std::to_string(funcs.size()) + ")", true);
auto fetcher = m_fetchProcessor;
auto bv = m_bv;
// TODO: Too many captures in this thing lol.
WorkerInteractiveEnqueue(
- [fetcher, bv, funcs = std::move(funcs), batchSize, rerunMatcher, task, allowedTags]() mutable {
+ [fetcher, bv, funcs = std::move(funcs), rerunMatcher, task, allowedTags]() mutable {
+ const auto batchSize = GetBatchSizeFromView(bv);
size_t processed = 0;
- size_t batchIndex = 0;
-
while (processed < funcs.size())
{
+ if (task->IsCancelled())
+ break;
const size_t remaining = funcs.size() - processed;
const size_t thisBatchCount = std::min(batchSize, remaining);
-
for (size_t i = 0; i < thisBatchCount; ++i)
fetcher->AddPendingFunction(funcs[processed + i]);
-
fetcher->FetchPendingFunctions(allowedTags);
-
- ++batchIndex;
processed += thisBatchCount;
-
- task->SetProgressText("Fetching WARP functions (" + std::to_string(batchIndex) + " / "
- + std::to_string((funcs.size() + batchSize - 1) / batchSize) + ")");
+ task->SetProgressText("Fetching WARP functions (" + std::to_string(processed) + " / " + std::to_string(funcs.size()) + ")");
}
task->Finish();
- // TODO: Print how long it took?
- Logger("WARP Fetcher").LogInfo("Finished fetching WARP functions...");
+ Logger("WARP Fetcher").LogInfo("Finished fetching WARP functions in %d seconds...", task->GetRuntimeSeconds());
if (rerunMatcher && bv)
Warp::RunMatcher(*bv);
diff --git a/plugins/warp/ui/shared/fetchdialog.h b/plugins/warp/ui/shared/fetchdialog.h
index c642d31e..72b8c57e 100644
--- a/plugins/warp/ui/shared/fetchdialog.h
+++ b/plugins/warp/ui/shared/fetchdialog.h
@@ -22,7 +22,6 @@ class WarpFetchDialog : public QDialog
QPushButton* m_removeTagBtn;
QPushButton* m_resetTagBtn;
- QSpinBox* m_batchSize;
QCheckBox* m_rerunMatcher;
QCheckBox* m_clearProcessed;
@@ -51,7 +50,7 @@ private:
std::vector<Warp::SourceTag> collectTags() const;
void runBatchedFetch(const std::optional<size_t>& containerIndex, const std::vector<Warp::SourceTag>& allowedTags,
- size_t batchSize, bool rerunMatcher);
+ bool rerunMatcher);
};
void RegisterWarpFetchFunctionsCommand();
diff --git a/plugins/warp/ui/shared/fetcher.cpp b/plugins/warp/ui/shared/fetcher.cpp
index 51ab018c..2eab910a 100644
--- a/plugins/warp/ui/shared/fetcher.cpp
+++ b/plugins/warp/ui/shared/fetcher.cpp
@@ -63,22 +63,40 @@ void WarpFetcher::FetchPendingFunctions(const std::vector<Warp::SourceTag>& allo
// Because we must fetch for a single target we map the function guids to the associated platform to perform fetches
// for each.
- std::map<PlatformRef, std::vector<Warp::FunctionGUID>> platformMappedGuids;
+ std::map<PlatformRef, std::unordered_set<Warp::FunctionGUID>> platformMappedGuidSet;
+ std::map<PlatformRef, std::unordered_set<Warp::ConstraintGUID>> platformMappedConstraintSet;
for (const auto& func : requests)
{
- const auto guid = Warp::GetAnalysisFunctionGUID(*func);
- if (!guid.has_value())
+ const auto warpFunc = Warp::Function::Get(*func);
+ if (!warpFunc)
continue;
auto platform = func->GetPlatform();
- platformMappedGuids[platform].push_back(guid.value());
+ platformMappedGuidSet[platform].insert(warpFunc->GetGUID());
+
+ // We want to keep track of the guids so we can constrain the server response to only return functions with any of them.
+ const auto constraints = warpFunc->GetConstraints();
+ std::vector<Warp::ConstraintGUID> constraintGuids;
+ constraintGuids.reserve(constraints.size());
+ for (const auto& constraint : constraints)
+ constraintGuids.push_back(constraint.guid);
+ platformMappedConstraintSet[platform].insert(constraintGuids.begin(), constraintGuids.end());
}
+ std::map<PlatformRef, std::vector<Warp::FunctionGUID>> platformMappedGuids;
+ for (const auto& [platform, guids] : platformMappedGuidSet)
+ platformMappedGuids[platform] = std::vector(guids.begin(), guids.end());
+
+ // We keep them in the set above so we don't duplicate a bunch for functions with the same set of constraint guids.
+ std::map<PlatformRef, std::vector<Warp::ConstraintGUID>> platformMappedConstraints;
+ for (const auto& [platform, guids] : platformMappedConstraintSet)
+ platformMappedConstraints[platform] = std::vector(guids.begin(), guids.end());
+
for (const auto& [platform, guids] : platformMappedGuids)
{
m_logger->LogDebugF("Fetching {} functions for platform {}", guids.size(), platform->GetName());
auto target = Warp::Target::FromPlatform(*platform);
for (const auto& container : Warp::Container::All())
- container->FetchFunctions(*target, guids, allowedTags);
+ container->FetchFunctions(*target, guids, allowedTags, platformMappedConstraints[platform]);
std::lock_guard<std::mutex> lock(m_requestMutex);
for (const auto& guid : guids)
diff --git a/plugins/warp/ui/shared/misc.h b/plugins/warp/ui/shared/misc.h
index 97adcab0..92e27fd8 100644
--- a/plugins/warp/ui/shared/misc.h
+++ b/plugins/warp/ui/shared/misc.h
@@ -138,10 +138,10 @@ inline void SetTagsToView(const BinaryViewRef& view, const std::vector<Warp::Sou
settings->Set(ALLOWED_TAGS_SETTING, tags, view);
}
-inline int GetBatchSizeFromView(const BinaryViewRef& view)
+inline size_t GetBatchSizeFromView(const BinaryViewRef& view)
{
auto settings = BinaryNinja::Settings::Instance();
if (!settings->Contains(BATCH_SIZE_SETTING))
- return 100;
+ return 10000;
return settings->Get<uint64_t>(BATCH_SIZE_SETTING, view);
} \ No newline at end of file