Skip to content

Commit

Permalink
Use phmap
Browse files Browse the repository at this point in the history
  • Loading branch information
yujincheng08 committed Oct 7, 2023
1 parent 7a6940e commit c18f276
Show file tree
Hide file tree
Showing 5 changed files with 63 additions and 81 deletions.
8 changes: 4 additions & 4 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ plugins {
alias(libs.plugins.lsplugin.publish)
}

val androidTargetSdkVersion by extra(33)
val androidTargetSdkVersion by extra(34)
val androidMinSdkVersion by extra(21)
val androidBuildToolsVersion by extra("33.0.2")
val androidCompileSdkVersion by extra(33)
val androidNdkVersion by extra("25.2.9519653")
val androidBuildToolsVersion by extra("34.0.0")
val androidCompileSdkVersion by extra(34)
val androidNdkVersion by extra("26.0.10792818")
val androidCmakeVersion by extra("3.22.1+")
20 changes: 8 additions & 12 deletions lsplant/src/main/jni/art/mirror/class.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,37 +28,33 @@ class Class {
}

using BackupMethods = std::list<std::tuple<art::ArtMethod *, void *>>;
inline static absl::flat_hash_map<const art::Thread *,
absl::flat_hash_map<const dex::ClassDef *, BackupMethods>>
inline static phmap::flat_hash_map<const art::Thread *,
phmap::flat_hash_map<const dex::ClassDef *, BackupMethods>>
backup_methods_;
inline static std::mutex backup_methods_lock_;

static void BackupClassMethods(const dex::ClassDef *class_def, art::Thread *self) {
std::list<std::tuple<art::ArtMethod *, void *>> out;
if (!class_def) return;
{
std::shared_lock lk(hooked_classes_lock_);
if (auto found = hooked_classes_.find(class_def); found != hooked_classes_.end())
[[unlikely]] {
for (auto method : found->second) {
hooked_classes_.if_contains(class_def, [&out](const auto &it) {
for (auto method : it.second) {
if (method->IsStatic()) {
LOGV("Backup hooked method %p because of initialization", method);
out.emplace_back(method, method->GetEntryPoint());
}
}
}
});
}
{
std::shared_lock lk(deoptimized_methods_lock_);
if (auto found = deoptimized_classes_.find(class_def);
found != deoptimized_classes_.end()) [[unlikely]] {
for (auto method : found->second) {
deoptimized_classes_.if_contains(class_def, [&out](const auto &it) {
for (auto method : it.second) {
if (method->IsStatic()) {
LOGV("Backup deoptimized method %p because of initialization", method);
out.emplace_back(method, method->GetEntryPoint());
}
}
}
});
}
if (!out.empty()) [[unlikely]] {
std::unique_lock lk(backup_methods_lock_);
Expand Down
72 changes: 33 additions & 39 deletions lsplant/src/main/jni/common.hpp
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
#pragma once

#include <absl/container/flat_hash_map.h>
#include <absl/container/flat_hash_set.h>
#include <parallel_hashmap/phmap.h>
#include <sys/system_properties.h>

#include <list>
Expand Down Expand Up @@ -104,44 +103,47 @@ class Class;

namespace {
// target, backup
inline absl::flat_hash_map<art::ArtMethod *, std::pair<jobject, art::ArtMethod *>> hooked_methods_;
inline std::shared_mutex hooked_methods_lock_;
template <class K, class V, class Hash = phmap::priv::hash_default_hash<K>,
class Eq = phmap::priv::hash_default_eq<K>,
class Alloc = phmap::priv::Allocator<phmap::priv::Pair<const K, V>>, size_t N = 4>
using SharedHashMap = phmap::parallel_flat_hash_map<K, V, Hash, Eq, Alloc, N, std::shared_mutex>;

inline absl::flat_hash_map<const art::dex::ClassDef *, absl::flat_hash_set<art::ArtMethod *>>
template <class T, class Hash = phmap::priv::hash_default_hash<T>,
class Eq = phmap::priv::hash_default_eq<T>, class Alloc = phmap::priv::Allocator<T>,
size_t N = 4>
using SharedHashSet = phmap::parallel_flat_hash_set<T, Hash, Eq, Alloc, N, std::shared_mutex>;

inline SharedHashMap<art::ArtMethod *, std::pair<jobject, art::ArtMethod *>> hooked_methods_;

inline SharedHashMap<const art::dex::ClassDef *, phmap::flat_hash_set<art::ArtMethod *>>
hooked_classes_;
inline std::shared_mutex hooked_classes_lock_;

inline absl::flat_hash_set<art::ArtMethod *> deoptimized_methods_set_;
inline std::shared_mutex deoptimized_methods_lock_;
inline SharedHashSet<art::ArtMethod *> deoptimized_methods_set_;

inline absl::flat_hash_map<const art::dex::ClassDef *, absl::flat_hash_set<art::ArtMethod *>>
inline SharedHashMap<const art::dex::ClassDef *, phmap::flat_hash_set<art::ArtMethod *>>
deoptimized_classes_;
inline std::shared_mutex deoptimized_classes_lock_;

inline std::list<std::pair<art::ArtMethod *, art::ArtMethod *>> jit_movements_;
inline std::shared_mutex jit_movements_lock_;
} // namespace

inline art::ArtMethod *IsHooked(art::ArtMethod *art_method, bool including_backup = false) {
std::shared_lock lk(hooked_methods_lock_);
if (auto it = hooked_methods_.find(art_method);
it != hooked_methods_.end() && (!including_backup || it->second.first)) {
return it->second.second;
}
return nullptr;
art::ArtMethod *backup = nullptr;
hooked_methods_.if_contains(art_method, [&backup, &including_backup](const auto &it) {
if (!including_backup || it.second.first) backup = it.second.second;
});
return backup;
}

inline art::ArtMethod *IsBackup(art::ArtMethod *art_method) {
std::shared_lock lk(hooked_methods_lock_);
if (auto it = hooked_methods_.find(art_method);
it != hooked_methods_.end() && !it->second.first) {
return it->second.second;
}
art::ArtMethod *backup = nullptr;
hooked_methods_.if_contains(art_method, [&backup](const auto &it) {
if (!it.second.first) backup = it.second.second;
});
return nullptr;
}

inline bool IsDeoptimized(art::ArtMethod *art_method) {
std::shared_lock lk(deoptimized_methods_lock_);
return deoptimized_methods_set_.contains(art_method);
}

Expand All @@ -152,26 +154,18 @@ inline std::list<std::pair<art::ArtMethod *, art::ArtMethod *>> GetJitMovements(

inline void RecordHooked(art::ArtMethod *target, const art::dex::ClassDef *class_def,
jobject reflected_backup, art::ArtMethod *backup) {
{
std::unique_lock lk(hooked_classes_lock_);
hooked_classes_[class_def].emplace(target);
}
{
std::unique_lock lk(hooked_methods_lock_);
hooked_methods_[target] = {reflected_backup, backup};
hooked_methods_[backup] = {nullptr, target};
}
hooked_classes_.lazy_emplace_l(
class_def, [&target](auto &it) { it.second.emplace(target); },
[&class_def, &target](const auto &ctor) {
ctor(class_def, phmap::flat_hash_set<art::ArtMethod *>{target});
});
hooked_methods_.insert({std::make_pair(target, std::make_pair(reflected_backup, backup)),
std::make_pair(backup, std::make_pair(nullptr, target))});
}

inline void RecordDeoptimized(const art::dex::ClassDef *class_def, art::ArtMethod *art_method) {
{
std::unique_lock lk(deoptimized_classes_lock_);
deoptimized_classes_[class_def].emplace(art_method);
}
{
std::unique_lock lk(deoptimized_methods_lock_);
deoptimized_methods_set_.insert(art_method);
}
{ deoptimized_classes_[class_def].emplace(art_method); }
deoptimized_methods_set_.insert(art_method);
}

inline void RecordJitMovement(art::ArtMethod *target, art::ArtMethod *backup) {
Expand Down
42 changes: 17 additions & 25 deletions lsplant/src/main/jni/lsplant.cc
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,8 @@ bool InitJNI(JNIEnv *env) {
return false;
}
if (method_get_return_type =
JNI_GetMethodID(env, JNI_FindClass(env, "java/lang/reflect/Method"), "getReturnType", "()Ljava/lang/Class;");
JNI_GetMethodID(env, JNI_FindClass(env, "java/lang/reflect/Method"), "getReturnType",
"()Ljava/lang/Class;");
!method_get_return_type) {
LOGE("Failed to find getReturnType method");
return false;
Expand Down Expand Up @@ -620,7 +621,7 @@ struct JavaDebuggableGuard {
if (count.compare_exchange_strong(expected, 1, std::memory_order_acq_rel,
std::memory_order_acquire)) {
Runtime::Current()->SetJavaDebuggable(
Runtime::RuntimeDebugState::kJavaDebuggableAtInit);
Runtime::RuntimeDebugState::kJavaDebuggableAtInit);
count.fetch_add(1, std::memory_order_release);
count.notify_all();
break;
Expand All @@ -642,7 +643,7 @@ struct JavaDebuggableGuard {
if (count.compare_exchange_strong(expected, 1, std::memory_order_acq_rel,
std::memory_order_acquire)) {
Runtime::Current()->SetJavaDebuggable(
Runtime::RuntimeDebugState::kNonJavaDebuggable);
Runtime::RuntimeDebugState::kNonJavaDebuggable);
count.fetch_sub(1, std::memory_order_release);
count.notify_all();
break;
Expand Down Expand Up @@ -776,28 +777,19 @@ using ::lsplant::IsHooked;
auto *target = ArtMethod::FromReflectedMethod(env, target_method);
jobject reflected_backup = nullptr;
art::ArtMethod *backup = nullptr;
{
std::unique_lock lk(hooked_methods_lock_);
if (auto it = hooked_methods_.find(target); it != hooked_methods_.end()) [[likely]] {
std::tie(reflected_backup, backup) = it->second;
if (reflected_backup == nullptr) {
LOGE("Unable to unhook a method that is not hooked");
return false;
}
hooked_methods_.erase(it->second.second);
hooked_methods_.erase(it);
}
}
{
std::unique_lock lk(hooked_classes_lock_);
if (auto it = hooked_classes_.find(target->GetDeclaringClass()->GetClassDef());
it != hooked_classes_.end()) {
it->second.erase(target);
if (it->second.empty()) {
hooked_classes_.erase(it);
}
}
}
if (!hooked_methods_.erase_if(target, [&reflected_backup, &backup](const auto &it) {
std::tie(reflected_backup, backup) = it.second;
return reflected_backup != nullptr;
})) {
LOGE("Unable to unhook a method that is not hooked");
return false;
}
// FIXME: not atomic, but should be fine
hooked_methods_.erase(backup);
hooked_classes_.erase_if(target->GetDeclaringClass()->GetClassDef(), [&target](auto &it) {
it.second.erase(target);
return it.second.empty();
});
auto *backup_method = env->FromReflectedMethod(reflected_backup);
env->DeleteGlobalRef(reflected_backup);
if (DoUnHook(target, backup)) {
Expand Down

0 comments on commit c18f276

Please sign in to comment.