summaryrefslogtreecommitdiff
path: root/plugins/warp/ui/shared/fetcher.cpp
blob: 767932e3cc2395cef7cf1bb34064ef48ea1703aa (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
#include "fetcher.h"

#include <QSettings>

WarpFetcher::WarpFetcher()
{
    m_logger = new BinaryNinja::Logger("WARP Fetcher");
    QSettings qtSettings;
    const QString key = "warp/allowedTags";

    QStringList tags = qtSettings.value(key).toStringList();
    if (tags.isEmpty()) {
        tags = QStringList{ "official", "trusted" };
        qtSettings.setValue(key, tags);
        qtSettings.sync();
    }

    std::vector<Warp::SourceTag> initialTags;
    initialTags.reserve(tags.size());
    for (const auto& t : tags)
        initialTags.emplace_back(t.trimmed().toStdString());

    SetTags(initialTags);
}

void WarpFetcher::AddPendingFunction(const FunctionRef &func)
{
    std::lock_guard<std::mutex> lock(m_requestMutex);
    const auto guid = Warp::GetAnalysisFunctionGUID(*func);
    if (!guid.has_value() || m_processedGuids.contains(*guid))
        return;
    m_pendingRequests.push_back(func);
}

std::vector<FunctionRef> WarpFetcher::FlushPendingFunctions()
{
    std::lock_guard<std::mutex> lock(m_requestMutex);
    std::vector<FunctionRef> requests = std::move(m_pendingRequests);
    m_pendingRequests.clear();
    return requests;
}

void WarpFetcher::ExecuteCompletionCallback()
{
    BinaryNinja::ExecuteOnMainThread([this]() {
        // TODO: Holding the mutex here is dangerous!
        std::lock_guard<std::mutex> lock(m_requestMutex);
        m_completionCallbacks.erase(
            std::ranges::remove_if(m_completionCallbacks,
                                   [](const auto &cb) { return cb() != RemoveCallback; }).begin(),
            m_completionCallbacks.end());
    });
}

std::shared_ptr<WarpFetcher> WarpFetcher::Global()
{
    static auto global = std::make_shared<WarpFetcher>();
    return global;
}

void WarpFetcher::FetchPendingFunctions()
{
    m_requestInProgress = true;
    const auto requests = FlushPendingFunctions();
    if (requests.empty())
    {
        m_logger->LogDebug("No pending requests to fetch... skipping");
        m_requestInProgress = false;
        return;
    }

    const auto start_time = std::chrono::high_resolution_clock::now();

    // 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;
    for (const auto &func: requests)
    {
        const auto guid = Warp::GetAnalysisFunctionGUID(*func);
        if (!guid.has_value())
            continue;
        auto platform = func->GetPlatform();
        platformMappedGuids[platform].push_back(guid.value());
    }

    const auto tags = GetTags();
    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, tags);

        std::lock_guard<std::mutex> lock(m_requestMutex);
        for (const auto &guid: guids)
            m_processedGuids.insert(guid);
    }

    m_requestInProgress = false;
    ExecuteCompletionCallback();
    const auto end_time = std::chrono::high_resolution_clock::now();
    const std::chrono::duration<double> elapsed_time = end_time - start_time;
    m_logger->LogDebug("Fetch batch took %f seconds", elapsed_time.count());
}

void WarpFetcher::ClearProcessed()
{
    m_logger->LogInfoF("Clearing {} processed functions from cache...", m_processedGuids.size());
    m_processedGuids.clear();
}