Skip to content

Commit

Permalink
ObjectExplorer: Add sortable callers for method hooks
Browse files Browse the repository at this point in the history
  • Loading branch information
praydog committed Mar 27, 2024
1 parent 0a4a26d commit c6ebac2
Show file tree
Hide file tree
Showing 2 changed files with 80 additions and 21 deletions.
88 changes: 67 additions & 21 deletions src/mods/tools/ObjectExplorer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -645,7 +645,7 @@ void ObjectExplorer::on_frame() {
// on_frame is just going to be a way to display
// the pinned objects in a separate window

ImGui::SetNextWindowSize(ImVec2(400, 400), ImGuiCond_::ImGuiCond_Once);
ImGui::SetNextWindowSize(ImVec2(400, 800), ImGuiCond_::ImGuiCond_Once);
if (ImGui::Begin("Hooked methods", &open)) {
display_hooks();

Expand Down Expand Up @@ -766,33 +766,79 @@ void ObjectExplorer::display_hooks() {
if (made_node) {
ImGui::Checkbox("Skip function call", &h.skip);
ImGui::TextWrapped("Call count: %i", h.call_count);

ImGui::SameLine();
const float delta_ms = std::chrono::duration_cast<std::chrono::duration<float, std::milli>>(h.last_call_delta).count();
const float total_ms = std::chrono::duration_cast<std::chrono::duration<float, std::milli>>(h.total_call_time).count();
ImGui::TextWrapped("Call Time (ms): Delta %f, Total %f", delta_ms, total_ms);
if (ImGui::TreeNode("Callers")) {
for (auto& caller : h.callers) {
const auto& context = h.callers_context[caller];
const auto declaring_type = caller->get_declaring_type();
//auto method_name = declaring_type != nullptr ? declaring_type->get_full_name() + "." + caller->get_name() : caller->get_name();
//method_name += " [" + std::to_string(context.call_count) + "]";
const auto call_count = std::string("[") + std::to_string(context.call_count) + "]";

ImGui::TextUnformatted(call_count.c_str());
ImGui::SameLine();
this->attempt_display_method(nullptr, *caller, true);
}
ImGui::TextWrapped("Time (ms): Delta %f, Total %f", delta_ms, total_ms);

if (ImGui::TreeNode("Info")) {
ImGui::SetNextItemOpen(true, ImGuiCond_::ImGuiCond_Once);
if (ImGui::TreeNode("Callers")) {
// sort callers combo
if (ImGui::BeginCombo("Sort Callers by", HookedMethod::s_sort_callers_names[(uint8_t)h.sort_callers_method])) {
for (int i = 0; i < HookedMethod::s_sort_callers_names.size(); i++) {
const bool is_selected = (h.sort_callers_method == (HookedMethod::SortCallersMethod)i);

if (ImGui::Selectable(HookedMethod::s_sort_callers_names[i], is_selected)) {
h.sort_callers_method = (HookedMethod::SortCallersMethod)i;
}

ImGui::TreePop();
}
if (is_selected) {
ImGui::SetItemDefaultFocus();
}
}

if (ImGui::TreeNode("Return Addresses")) {
for (auto addr : h.return_addresses) {
ImGui::Text("0x%p", addr);
ImGui::EndCombo();
}

std::vector<sdk::REMethodDefinition*> callers_to_iterate{};

if (ImGui::IsItemClicked()) {
ImGui::SetClipboardText((std::stringstream{} << std::hex << addr).str().c_str());
for (auto& caller : h.callers) {
callers_to_iterate.push_back(caller);
}

switch (h.sort_callers_method) {
case HookedMethod::SortCallersMethod::CALL_COUNT:
std::sort(callers_to_iterate.begin(), callers_to_iterate.end(), [&h](const auto& a, const auto& b) {
return h.callers_context[a].call_count > h.callers_context[b].call_count;
});
break;
case HookedMethod::SortCallersMethod::METHOD_NAME:
std::sort(callers_to_iterate.begin(), callers_to_iterate.end(), [&h](const auto& a, const auto& b) {
return a->get_name() < b->get_name();
});
break;
default:
break;
};

for (auto& caller : callers_to_iterate) {
const auto& context = h.callers_context[caller];
const auto declaring_type = caller->get_declaring_type();
//auto method_name = declaring_type != nullptr ? declaring_type->get_full_name() + "." + caller->get_name() : caller->get_name();
//method_name += " [" + std::to_string(context.call_count) + "]";
const auto call_count = std::string("[") + std::to_string(context.call_count) + "]";

ImGui::TextUnformatted(call_count.c_str());
ImGui::SameLine();
this->attempt_display_method(nullptr, *caller, true);
}

ImGui::TreePop();
}

if (ImGui::TreeNode("Return Addresses")) {
for (auto addr : h.return_addresses) {
ImGui::Text("0x%p", addr);

if (ImGui::IsItemClicked()) {
ImGui::SetClipboardText((std::stringstream{} << std::hex << addr).str().c_str());
}
}
ImGui::TreePop();
}

ImGui::TreePop();
}

Expand Down
13 changes: 13 additions & 0 deletions src/mods/tools/ObjectExplorer.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -260,13 +260,26 @@ class ObjectExplorer : public Tool {
std::vector<std::function<void()>> m_frame_jobs{};

struct HookedMethod {
enum class SortCallersMethod : uint8_t {
NONE,
CALL_COUNT,
METHOD_NAME
};

static inline constexpr std::array<const char*, 3> s_sort_callers_names {
"None",
"Call Count",
"Method Name"
};

std::string name{};
sdk::REMethodDefinition* method{nullptr};
uintptr_t jitted_function{};
uintptr_t jitted_function_post{};
bool skip{false};
size_t hook_id{};
uint32_t call_count{};
SortCallersMethod sort_callers_method{SortCallersMethod::NONE};

std::unordered_set<uintptr_t> return_addresses{};
std::unordered_map<uintptr_t, sdk::REMethodDefinition*> return_addresses_to_methods{};
Expand Down

0 comments on commit c6ebac2

Please sign in to comment.