Skip to content

Commit

Permalink
Back out "Remove PrintKotlinStatsPass"
Browse files Browse the repository at this point in the history
Summary:
Original commit changeset: 5187910212e1

Original Phabricator Diff: D47980468
please refer to the discussion in originial diff

Reviewed By: thezhangwei

Differential Revision: D48135972

fbshipit-source-id: 95458583c2406d659fc54784a3fcbcebb5724010
  • Loading branch information
beicy authored and facebook-github-bot committed Aug 8, 2023
1 parent 3dd6be8 commit 23d0500
Show file tree
Hide file tree
Showing 7 changed files with 511 additions and 1 deletion.
3 changes: 2 additions & 1 deletion Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -417,8 +417,9 @@ libopt_la_SOURCES = \
opt/partial-application/PartialApplication.cpp \
opt/peephole/Peephole.cpp \
opt/peephole/RedundantCheckCastRemover.cpp \
opt/print-kotlin-stats/PrintKotlinStats.cpp \
opt/print-members/PrintMembers.cpp \
opt/reachable-natives/ReachableNatives.cpp \
opt/reachable-natives/ReachableNatives.cpp \
opt/rebindrefs/ReBindRefs.cpp \
opt/regalloc/RegAlloc.cpp \
opt/regalloc-fast/FastRegAlloc.cpp \
Expand Down
8 changes: 8 additions & 0 deletions libredex/ReferencedState.h
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,9 @@ class ReferencedState {
// Whether this member is an outlined class or method.
bool m_outlined : 1;

// Is this member is a kotlin class or method
bool m_is_kotlin : 1;

bool m_name_used : 1;

// Whether a field is used to indicate that an sget cannot be removed
Expand Down Expand Up @@ -128,6 +131,7 @@ class ReferencedState {
m_immutable_getter = false;
m_pure_method = false;
m_outlined = false;
m_is_kotlin = false;

m_name_used = false;

Expand Down Expand Up @@ -215,6 +219,8 @@ class ReferencedState {
other.inner_struct.m_immutable_getter;
this->inner_struct.m_pure_method =
this->inner_struct.m_pure_method & other.inner_struct.m_pure_method;
this->inner_struct.m_is_kotlin =
this->inner_struct.m_is_kotlin & other.inner_struct.m_is_kotlin;
this->inner_struct.m_outlined =
this->inner_struct.m_outlined & other.inner_struct.m_outlined;

Expand Down Expand Up @@ -393,6 +399,8 @@ class ReferencedState {
bool outlined() const { return inner_struct.m_outlined; }
void set_outlined() { inner_struct.m_outlined = true; }
void reset_outlined() { inner_struct.m_outlined = false; }
bool is_cls_kotlin() const { return inner_struct.m_is_kotlin; }
void set_cls_kotlin() { inner_struct.m_is_kotlin = true; }
void set_name_used() { inner_struct.m_name_used = true; }
bool name_used() { return inner_struct.m_name_used; }

Expand Down
246 changes: 246 additions & 0 deletions opt/print-kotlin-stats/PrintKotlinStats.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,246 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

#include "PrintKotlinStats.h"
#include "DexUtil.h"
#include "IRCode.h"
#include "KotlinNullCheckMethods.h"
#include "PassManager.h"
#include "Show.h"
#include "Walkers.h"

namespace {
constexpr const char* LAZY_SIGNATURE = "Lkotlin/Lazy;";
constexpr const char* R_PROP_SIGNATURE = "Lkotlin/properties/ReadProperty;";
constexpr const char* W_PROP_SIGNATURE = "Lkotlin/properties/WriteProperty;";
constexpr const char* RW_PROP_SIGNATURE =
"Lkotlin/properties/ReadWriteProperty;";
constexpr const char* KPROPERTY_ARRAY = "[Lkotlin/reflect/KProperty;";
constexpr const char* KOTLIN_LAMBDA = "Lkotlin/jvm/internal/Lambda;";
constexpr const char* DI_BASE = "Lcom/facebook/inject/AbstractLibraryModule;";
constexpr const char* CONTINUATION_IMPL =
"Lkotlin/coroutines/jvm/internal/ContinuationImpl;";

// Check if cls is from Kotlin source
bool is_kotlin_class(DexClass* cls) {
auto src_string = cls->get_source_file();
if (src_string && boost::algorithm::ends_with(src_string->str(), ".kt")) {
return true;
}
return false;
}

// Check cls name is in anonymous format
// Anonymous cls name ends with \$[0-9]*
bool is_anonymous(std::string_view name) {
auto last = name.find_last_of('$');
if (last == std::string::npos) {
return false;
}
// Except for the ; at the end
for (size_t i = last + 1; i < name.length() - 1; ++i) {
if (name[i] < '0' || name[i] > '9') {
return false;
}
}
return true;
}

} // namespace

// Setup types/strings needed for the pass
void PrintKotlinStats::setup() {
m_kotlin_null_assertions =
kotlin_nullcheck_wrapper::get_kotlin_null_assertions();
m_kotlin_lambdas_base = DexType::get_type(KOTLIN_LAMBDA);
m_kotlin_coroutin_continuation_base = DexType::get_type(CONTINUATION_IMPL);
m_di_base = DexType::get_type(DI_BASE);
m_instance = DexString::make_string("INSTANCE");
}

// Annotate Kotlin classes before StripDebugInfoPass removes it
void PrintKotlinStats::eval_pass(DexStoresVector& stores,
ConfigFiles&,
PassManager&) {
Scope scope = build_class_scope(stores);
setup();
walk::parallel::classes(scope, [&](DexClass* cls) {
if (is_kotlin_class(cls)) {
cls->rstate.set_cls_kotlin();
}
});
}

void PrintKotlinStats::run_pass(DexStoresVector& stores,
ConfigFiles&,
PassManager& mgr) {
Scope scope = build_class_scope(stores);
std::unordered_set<DexType*> delegate_types{
DexType::get_type(KPROPERTY_ARRAY),
DexType::get_type(R_PROP_SIGNATURE),
DexType::get_type(W_PROP_SIGNATURE),
DexType::get_type(RW_PROP_SIGNATURE),
};
std::unordered_set<DexType*> lazy_delegate_types{
DexType::get_type(LAZY_SIGNATURE),
};

// Handle methods
m_stats = walk::parallel::methods<Stats>(
scope, [&](DexMethod* method) { return handle_method(method); });

// Handle fields
// Count delegated properties
walk::fields(scope, [&](DexField* field) {
auto typ = field->get_type();
if (lazy_delegate_types.count(typ)) {
m_stats.kotlin_lazy_delegates++;
}
if (delegate_types.count(typ)) {
m_stats.kotlin_delegates++;
}
});

// Handle classes
std::mutex mtx;
walk::parallel::classes(scope, [&](DexClass* cls) {
auto local_stats = handle_class(cls);
std::lock_guard g(mtx);
m_stats += local_stats;
});
m_stats.report(mgr);
}

PrintKotlinStats::Stats PrintKotlinStats::handle_class(DexClass* cls) {
Stats stats;
bool is_lambda = false;
if (cls->get_super_class() == m_kotlin_lambdas_base) {
stats.kotlin_lambdas++;
is_lambda = true;
}
if (cls->get_super_class() == m_kotlin_coroutin_continuation_base) {
stats.kotlin_coroutine_continuation_base++;
}

if (cls->get_super_class() == m_di_base) {
stats.di_generated_class++;
}

for (auto* field : cls->get_sfields()) {
if (field->get_name() == m_instance &&
field->get_type() == cls->get_type()) {
if (is_lambda) {
stats.kotlin_non_capturing_lambda++;
}
stats.kotlin_class_with_instance++;
}
}
if (cls->rstate.is_cls_kotlin()) {
stats.kotlin_class++;
for (auto* method : cls->get_all_methods()) {
if (boost::algorithm::ends_with(method->get_name()->str(), "$default")) {
stats.kotlin_default_arg_method++;
}
}
if (is_anonymous(cls->get_name()->str())) {
stats.kotlin_anonymous_class++;
}
if (boost::algorithm::ends_with(cls->get_name()->str(), "$Companion;")) {
stats.kotlin_companion_class++;
}
}
return stats;
}

PrintKotlinStats::Stats PrintKotlinStats::handle_method(DexMethod* method) {
Stats stats;

if (!method->get_code()) {
return stats;
}

DexClass* cls = type_class(method->get_class());
if (!cls) {
return stats;
}

if (method->get_access() & ACC_PUBLIC) {
auto* arg_types = method->get_proto()->get_args();
for (auto arg_type : *arg_types) {
if (cls->rstate.is_cls_kotlin()) {
stats.kotlin_public_param_objects++;
} else {
stats.java_public_param_objects++;
}
}
}

auto code = method->get_code();

for (const auto& it : InstructionIterable(code)) {
auto insn = it.insn;
switch (insn->opcode()) {
case OPCODE_INVOKE_STATIC: {
auto called_method = insn->get_method();
if (m_kotlin_null_assertions.count(called_method)) {
stats.kotlin_null_check_insns++;
}
} break;
default:
break;
}
}
return stats;
}

void PrintKotlinStats::Stats::report(PassManager& mgr) const {
mgr.incr_metric("kotlin_null_check_insns", kotlin_null_check_insns);
mgr.incr_metric("java_public_param_objects", java_public_param_objects);
mgr.incr_metric("kotlin_public_param_objects", kotlin_public_param_objects);
mgr.incr_metric("no_of_delegates", kotlin_delegates);
mgr.incr_metric("no_of_lazy_delegates", kotlin_lazy_delegates);
mgr.incr_metric("kotlin_lambdas", kotlin_lambdas);
mgr.incr_metric("kotlin_non_capturing_lambda", kotlin_non_capturing_lambda);
mgr.incr_metric("kotlin_classes_with_instance", kotlin_class_with_instance);
mgr.incr_metric("kotlin_class", kotlin_class);
mgr.incr_metric("Kotlin_anonymous_classes", kotlin_anonymous_class);
mgr.incr_metric("kotlin_companion_class", kotlin_companion_class);
mgr.incr_metric("di_generated_class", di_generated_class);
mgr.incr_metric("kotlin_default_arg_method", kotlin_default_arg_method);
mgr.incr_metric("kotlin_coroutine_continuation_base",
kotlin_coroutine_continuation_base);

TRACE(KOTLIN_STATS, 1, "KOTLIN_STATS: kotlin_null_check_insns = %zu",
kotlin_null_check_insns);
TRACE(KOTLIN_STATS, 1, "KOTLIN_STATS: java_public_param_objects = %zu",
java_public_param_objects);
TRACE(KOTLIN_STATS, 1, "KOTLIN_STATS: kotlin_public_param_objects = %zu",
kotlin_public_param_objects);
TRACE(KOTLIN_STATS, 1, "KOTLIN_STATS: no_of_delegates = %zu",
kotlin_delegates);
TRACE(KOTLIN_STATS, 1, "KOTLIN_STATS: no_of_lazy_delegates = %zu",
kotlin_lazy_delegates);
TRACE(KOTLIN_STATS, 1, "KOTLIN_STATS: kotlin_lambdas = %zu", kotlin_lambdas);
TRACE(KOTLIN_STATS, 1, "KOTLIN_STATS: kotlin_non_capturing_lambda = %zu",
kotlin_non_capturing_lambda);
TRACE(KOTLIN_STATS, 1, "KOTLIN_STATS: kotlin_class_with_instance = %zuu",
kotlin_class_with_instance);
TRACE(KOTLIN_STATS, 1, "KOTLIN_STATS: kotlin_class = %zu", kotlin_class);
TRACE(KOTLIN_STATS, 1, "KOTLIN_STATS: kotlin_anonymous_class = %zu",
kotlin_anonymous_class);
TRACE(KOTLIN_STATS, 1, "KOTLIN_STATS: kotlin_companion_class = %zu",
kotlin_companion_class);
TRACE(KOTLIN_STATS, 1, "KOTLIN_STATS: di_generated_class = %zu",
di_generated_class);
TRACE(KOTLIN_STATS, 1, "KOTLIN_STATS: kotlin_default_arg_method = %zu",
kotlin_default_arg_method);
TRACE(KOTLIN_STATS, 1,
"KOTLIN_STATS: kotlin_coroutine_continuation_base = %zu",
kotlin_coroutine_continuation_base);
}

static PrintKotlinStats s_pass;
87 changes: 87 additions & 0 deletions opt/print-kotlin-stats/PrintKotlinStats.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

#pragma once

#include "DexClass.h"
#include "DexUtil.h"
#include "Pass.h"

class PrintKotlinStats : public Pass {

public:
struct Stats {
size_t unknown_null_check_insns{0};
size_t kotlin_null_check_insns{0};
size_t java_public_param_objects{0};
size_t kotlin_public_param_objects{0};
size_t kotlin_delegates{0};
size_t kotlin_lazy_delegates{0};
size_t kotlin_lambdas{0};
size_t kotlin_non_capturing_lambda{0};
size_t kotlin_class_with_instance{0};
size_t kotlin_class{0};
size_t kotlin_anonymous_class{0};
size_t kotlin_companion_class{0};
size_t di_generated_class{0};
size_t kotlin_default_arg_method{0};
size_t kotlin_coroutine_continuation_base{0};

Stats& operator+=(const Stats& that) {
unknown_null_check_insns += that.unknown_null_check_insns;
kotlin_null_check_insns += that.kotlin_null_check_insns;
java_public_param_objects += that.java_public_param_objects;
kotlin_public_param_objects += that.kotlin_public_param_objects;
kotlin_delegates += that.kotlin_delegates;
kotlin_lazy_delegates += that.kotlin_lazy_delegates;
kotlin_lambdas += that.kotlin_lambdas;
kotlin_non_capturing_lambda += that.kotlin_non_capturing_lambda;
kotlin_class_with_instance += that.kotlin_class_with_instance;
kotlin_class += that.kotlin_class;
kotlin_anonymous_class += that.kotlin_anonymous_class;
kotlin_companion_class += that.kotlin_companion_class;
di_generated_class += that.di_generated_class;
kotlin_default_arg_method += that.kotlin_default_arg_method;
kotlin_coroutine_continuation_base +=
that.kotlin_coroutine_continuation_base;
return *this;
}

/// Updates metrics tracked by \p mgr corresponding to these statistics.
void report(PassManager& mgr) const;
};

PrintKotlinStats() : Pass("PrintKotlinStatsPass") {}

redex_properties::PropertyInteractions get_property_interactions()
const override {
using namespace redex_properties::interactions;
using namespace redex_properties::names;
return {
{DexLimitsObeyed, Preserves},
{HasSourceBlocks, Preserves},
{NoSpuriousGetClassCalls, Preserves},
{UltralightCodePatterns, Preserves},
};
}

void setup();
void eval_pass(DexStoresVector&, ConfigFiles&, PassManager&) override;
bool is_cfg_legacy() override { return true; }
void run_pass(DexStoresVector&, ConfigFiles&, PassManager&) override;
Stats handle_method(DexMethod* method);
Stats handle_class(DexClass* cls);
Stats get_stats() { return m_stats; }

private:
std::unordered_set<DexMethodRef*> m_kotlin_null_assertions;
DexType* m_kotlin_lambdas_base = nullptr;
DexType* m_kotlin_coroutin_continuation_base = nullptr;
const DexString* m_instance = nullptr;
DexType* m_di_base = nullptr;
Stats m_stats;
};
Loading

0 comments on commit 23d0500

Please sign in to comment.