diff options
author | 2017-09-21 16:21:43 +0100 | |
---|---|---|
committer | 2017-09-21 18:41:25 +0100 | |
commit | b8a55f8a62b1309efe52ec0290dfdcf60f34a550 (patch) | |
tree | 1f2a62ba7c4d4e95577414721cc274d0ee82fe7d | |
parent | 30744106517d64fb218ec5a96edbec797ad5a091 (diff) |
ART: Faster PrettyMethod().
Rewrite DexFile::PrettyMethod() to avoid copying strings.
This improves the performance, especially when requesting
the signature.
Avoid code duplication in ArtMethod::PrettyMethod() and
delegate to DexFile::PrettyMethod().
10 million invocations of ArtMethod/DexFile::PrettyMethod()
for "void Main.main(java.lang.String[] args)" with (+) or
without (-) signature, time in ms:
host/32-bit host/64-bit angler/32-bit angler/64-bit
AM+: 10407-> 5020 6374-> 3302 32413->13140 17558->10003
DF+: 7280-> 4259 3881-> 2828 19287-> 9331 10343-> 7375
AM-: 2682-> 1599 2025-> 1186 7206-> 4271 7447-> 4166
DF-: 861-> 871 653-> 640 1574-> 1430 1828-> 1712
Test: m test-art-host-gtest
Test: testrunner.py --host
Change-Id: Ifb79abe1a7f4fc6adc10a34f5d49dc6681d06699
-rw-r--r-- | runtime/art_method.cc | 30 | ||||
-rw-r--r-- | runtime/dex_file.cc | 26 | ||||
-rw-r--r-- | runtime/dex_file_test.cc | 85 | ||||
-rw-r--r-- | runtime/utils.cc | 63 | ||||
-rw-r--r-- | runtime/utils.h | 5 | ||||
-rw-r--r-- | runtime/utils_test.cc | 17 | ||||
-rw-r--r-- | test/GetMethodSignature/GetMethodSignature.java | 9 |
7 files changed, 123 insertions, 112 deletions
diff --git a/runtime/art_method.cc b/runtime/art_method.cc index 7d8dedab6f..c11f1a7fa4 100644 --- a/runtime/art_method.cc +++ b/runtime/art_method.cc @@ -777,26 +777,16 @@ std::string ArtMethod::PrettyMethod(ArtMethod* m, bool with_signature) { } std::string ArtMethod::PrettyMethod(bool with_signature) { - ArtMethod* m = this; - if (!m->IsRuntimeMethod()) { - m = m->GetInterfaceMethodIfProxy(Runtime::Current()->GetClassLinker()->GetImagePointerSize()); - } - std::string result(PrettyDescriptor(m->GetDeclaringClassDescriptor())); - result += '.'; - result += m->GetName(); - if (UNLIKELY(m->IsFastNative())) { - result += "!"; - } - if (with_signature) { - const Signature signature = m->GetSignature(); - std::string sig_as_string(signature.ToString()); - if (signature == Signature::NoSignature()) { - return result + sig_as_string; - } - result = PrettyReturnType(sig_as_string.c_str()) + " " + result + - PrettyArguments(sig_as_string.c_str()); - } - return result; + if (UNLIKELY(IsRuntimeMethod())) { + std::string result = GetDeclaringClassDescriptor(); + result += '.'; + result += GetName(); + // Do not add "<no signature>" even if `with_signature` is true. + return result; + } + ArtMethod* m = + GetInterfaceMethodIfProxy(Runtime::Current()->GetClassLinker()->GetImagePointerSize()); + return m->GetDexFile()->PrettyMethod(m->GetDexMethodIndex(), with_signature); } std::string ArtMethod::JniShortName() { diff --git a/runtime/dex_file.cc b/runtime/dex_file.cc index 6d1158260a..1a4752fd2f 100644 --- a/runtime/dex_file.cc +++ b/runtime/dex_file.cc @@ -1302,17 +1302,27 @@ std::string DexFile::PrettyMethod(uint32_t method_idx, bool with_signature) cons return StringPrintf("<<invalid-method-idx-%d>>", method_idx); } const DexFile::MethodId& method_id = GetMethodId(method_idx); - std::string result(PrettyDescriptor(GetMethodDeclaringClassDescriptor(method_id))); + std::string result; + const DexFile::ProtoId* proto_id = with_signature ? &GetProtoId(method_id.proto_idx_) : nullptr; + if (with_signature) { + AppendPrettyDescriptor(StringByTypeIdx(proto_id->return_type_idx_), &result); + result += ' '; + } + AppendPrettyDescriptor(GetMethodDeclaringClassDescriptor(method_id), &result); result += '.'; result += GetMethodName(method_id); if (with_signature) { - const Signature signature = GetMethodSignature(method_id); - std::string sig_as_string(signature.ToString()); - if (signature == Signature::NoSignature()) { - return result + sig_as_string; + result += '('; + const DexFile::TypeList* params = GetProtoParameters(*proto_id); + if (params != nullptr) { + const char* separator = ""; + for (uint32_t i = 0u, size = params->Size(); i != size; ++i) { + result += separator; + separator = ", "; + AppendPrettyDescriptor(StringByTypeIdx(params->GetTypeItem(i).type_idx_), &result); + } } - result = PrettyReturnType(sig_as_string.c_str()) + " " + result + - PrettyArguments(sig_as_string.c_str()); + result += ')'; } return result; } @@ -1327,7 +1337,7 @@ std::string DexFile::PrettyField(uint32_t field_idx, bool with_type) const { result += GetFieldTypeDescriptor(field_id); result += ' '; } - result += PrettyDescriptor(GetFieldDeclaringClassDescriptor(field_id)); + AppendPrettyDescriptor(GetFieldDeclaringClassDescriptor(field_id), &result); result += '.'; result += GetFieldName(field_id); return result; diff --git a/runtime/dex_file_test.cc b/runtime/dex_file_test.cc index 1a73062068..a7bf59eb29 100644 --- a/runtime/dex_file_test.cc +++ b/runtime/dex_file_test.cc @@ -423,28 +423,83 @@ TEST_F(DexFileTest, GetMethodSignature) { ASSERT_EQ("()V", signature); } - // Check both virtual methods. - ASSERT_EQ(2U, it.NumVirtualMethods()); - { + // Check all virtual methods. + struct Result { + const char* name; + const char* signature; + const char* pretty_method; + }; + static const Result results[] = { + { + "m1", + "(IDJLjava/lang/Object;)Ljava/lang/Float;", + "java.lang.Float GetMethodSignature.m1(int, double, long, java.lang.Object)" + }, + { // NOLINT [whitespace/braces] [4] + "m2", + "(ZSC)LGetMethodSignature;", + "GetMethodSignature GetMethodSignature.m2(boolean, short, char)" + }, + { // NOLINT [whitespace/braces] [4] + "m3", + "()V", + "void GetMethodSignature.m3()" + }, + { // NOLINT [whitespace/braces] [4] + "m4", + "(I)V", + "void GetMethodSignature.m4(int)" + }, + { // NOLINT [whitespace/braces] [4] + "m5", + "(II)V", + "void GetMethodSignature.m5(int, int)" + }, + { // NOLINT [whitespace/braces] [4] + "m6", + "(II[[I)V", + "void GetMethodSignature.m6(int, int, int[][])" + }, + { // NOLINT [whitespace/braces] [4] + "m7", + "(II[[ILjava/lang/Object;)V", + "void GetMethodSignature.m7(int, int, int[][], java.lang.Object)" + }, + { // NOLINT [whitespace/braces] [4] + "m8", + "(II[[ILjava/lang/Object;[[Ljava/lang/Object;)V", + "void GetMethodSignature.m8(int, int, int[][], java.lang.Object, java.lang.Object[][])" + }, + { // NOLINT [whitespace/braces] [4] + "m9", + "()I", + "int GetMethodSignature.m9()" + }, + { // NOLINT [whitespace/braces] [4] + "mA", + "()[[I", + "int[][] GetMethodSignature.mA()" + }, + { // NOLINT [whitespace/braces] [4] + "mB", + "()[[Ljava/lang/Object;", + "java.lang.Object[][] GetMethodSignature.mB()" + }, + }; + ASSERT_EQ(arraysize(results), it.NumVirtualMethods()); + for (const Result& r : results) { it.Next(); const DexFile::MethodId& method_id = raw->GetMethodId(it.GetMemberIndex()); const char* name = raw->StringDataByIdx(method_id.name_idx_); - ASSERT_STREQ("m1", name); + ASSERT_STREQ(r.name, name); std::string signature(raw->GetMethodSignature(method_id).ToString()); - ASSERT_EQ("(IDJLjava/lang/Object;)Ljava/lang/Float;", signature); - } + ASSERT_EQ(r.signature, signature); - { - it.Next(); - const DexFile::MethodId& method_id = raw->GetMethodId(it.GetMemberIndex()); - - const char* name = raw->StringDataByIdx(method_id.name_idx_); - ASSERT_STREQ("m2", name); - - std::string signature(raw->GetMethodSignature(method_id).ToString()); - ASSERT_EQ("(ZSC)LGetMethodSignature;", signature); + std::string plain_method = std::string("GetMethodSignature.") + r.name; + ASSERT_EQ(plain_method, raw->PrettyMethod(it.GetMemberIndex(), /* with_signature */ false)); + ASSERT_EQ(r.pretty_method, raw->PrettyMethod(it.GetMemberIndex(), /* with_signature */ true)); } } diff --git a/runtime/utils.cc b/runtime/utils.cc index 3fe18c7933..fc1c91ab22 100644 --- a/runtime/utils.cc +++ b/runtime/utils.cc @@ -148,7 +148,7 @@ bool PrintFileToLog(const std::string& file_name, LogSeverity level) { } } -std::string PrettyDescriptor(const char* descriptor) { +void AppendPrettyDescriptor(const char* descriptor, std::string* result) { // Count the number of '['s to get the dimensionality. const char* c = descriptor; size_t dim = 0; @@ -166,74 +166,41 @@ std::string PrettyDescriptor(const char* descriptor) { // To make life easier, we make primitives look like unqualified // reference types. switch (*c) { - case 'B': c = "byte;"; break; - case 'C': c = "char;"; break; - case 'D': c = "double;"; break; - case 'F': c = "float;"; break; - case 'I': c = "int;"; break; - case 'J': c = "long;"; break; - case 'S': c = "short;"; break; - case 'Z': c = "boolean;"; break; - case 'V': c = "void;"; break; // Used when decoding return types. - default: return descriptor; + case 'B': c = "byte;"; break; + case 'C': c = "char;"; break; + case 'D': c = "double;"; break; + case 'F': c = "float;"; break; + case 'I': c = "int;"; break; + case 'J': c = "long;"; break; + case 'S': c = "short;"; break; + case 'Z': c = "boolean;"; break; + case 'V': c = "void;"; break; // Used when decoding return types. + default: result->append(descriptor); return; } } // At this point, 'c' is a string of the form "fully/qualified/Type;" // or "primitive;". Rewrite the type with '.' instead of '/': - std::string result; const char* p = c; while (*p != ';') { char ch = *p++; if (ch == '/') { ch = '.'; } - result.push_back(ch); + result->push_back(ch); } // ...and replace the semicolon with 'dim' "[]" pairs: for (size_t i = 0; i < dim; ++i) { - result += "[]"; + result->append("[]"); } - return result; } -std::string PrettyArguments(const char* signature) { +std::string PrettyDescriptor(const char* descriptor) { std::string result; - result += '('; - CHECK_EQ(*signature, '('); - ++signature; // Skip the '('. - while (*signature != ')') { - size_t argument_length = 0; - while (signature[argument_length] == '[') { - ++argument_length; - } - if (signature[argument_length] == 'L') { - argument_length = (strchr(signature, ';') - signature + 1); - } else { - ++argument_length; - } - { - std::string argument_descriptor(signature, argument_length); - result += PrettyDescriptor(argument_descriptor.c_str()); - } - if (signature[argument_length] != ')') { - result += ", "; - } - signature += argument_length; - } - CHECK_EQ(*signature, ')'); - ++signature; // Skip the ')'. - result += ')'; + AppendPrettyDescriptor(descriptor, &result); return result; } -std::string PrettyReturnType(const char* signature) { - const char* return_type = strchr(signature, ')'); - CHECK(return_type != nullptr); - ++return_type; // Skip ')'. - return PrettyDescriptor(return_type); -} - std::string PrettyJavaAccessFlags(uint32_t access_flags) { std::string result; if ((access_flags & kAccPublic) != 0) { diff --git a/runtime/utils.h b/runtime/utils.h index 739681d446..9945298d81 100644 --- a/runtime/utils.h +++ b/runtime/utils.h @@ -81,13 +81,10 @@ std::string PrintableString(const char* utf8); // Returns a human-readable equivalent of 'descriptor'. So "I" would be "int", // "[[I" would be "int[][]", "[Ljava/lang/String;" would be // "java.lang.String[]", and so forth. +void AppendPrettyDescriptor(const char* descriptor, std::string* result); std::string PrettyDescriptor(const char* descriptor); std::string PrettyDescriptor(Primitive::Type type); -// Utilities for printing the types for method signatures. -std::string PrettyArguments(const char* signature); -std::string PrettyReturnType(const char* signature); - // Returns a human-readable version of the Java part of the access flags, e.g., "private static " // (note the trailing whitespace). std::string PrettyJavaAccessFlags(uint32_t access_flags); diff --git a/runtime/utils_test.cc b/runtime/utils_test.cc index ee8eb363f1..decb243c08 100644 --- a/runtime/utils_test.cc +++ b/runtime/utils_test.cc @@ -91,23 +91,6 @@ TEST_F(UtilsTest, PrettyDescriptor_PrimitiveScalars) { EXPECT_EQ("short", PrettyDescriptor("S")); } -TEST_F(UtilsTest, PrettyArguments) { - EXPECT_EQ("()", PrettyArguments("()V")); - EXPECT_EQ("(int)", PrettyArguments("(I)V")); - EXPECT_EQ("(int, int)", PrettyArguments("(II)V")); - EXPECT_EQ("(int, int, int[][])", PrettyArguments("(II[[I)V")); - EXPECT_EQ("(int, int, int[][], java.lang.Poop)", PrettyArguments("(II[[ILjava/lang/Poop;)V")); - EXPECT_EQ("(int, int, int[][], java.lang.Poop, java.lang.Poop[][])", PrettyArguments("(II[[ILjava/lang/Poop;[[Ljava/lang/Poop;)V")); -} - -TEST_F(UtilsTest, PrettyReturnType) { - EXPECT_EQ("void", PrettyReturnType("()V")); - EXPECT_EQ("int", PrettyReturnType("()I")); - EXPECT_EQ("int[][]", PrettyReturnType("()[[I")); - EXPECT_EQ("java.lang.Poop", PrettyReturnType("()Ljava/lang/Poop;")); - EXPECT_EQ("java.lang.Poop[][]", PrettyReturnType("()[[Ljava/lang/Poop;")); -} - TEST_F(UtilsTest, PrettyTypeOf) { ScopedObjectAccess soa(Thread::Current()); EXPECT_EQ("null", mirror::Object::PrettyTypeOf(nullptr)); diff --git a/test/GetMethodSignature/GetMethodSignature.java b/test/GetMethodSignature/GetMethodSignature.java index c2ba948d60..d2f96bbb96 100644 --- a/test/GetMethodSignature/GetMethodSignature.java +++ b/test/GetMethodSignature/GetMethodSignature.java @@ -17,4 +17,13 @@ class GetMethodSignature { Float m1(int a, double b, long c, Object d) { return null; } GetMethodSignature m2(boolean x, short y, char z) { return null; } + void m3() { } + void m4(int i) { } + void m5(int i, int j) { } + void m6(int i, int j, int[][] array1) { } + void m7(int i, int j, int[][] array1, Object o) { } + void m8(int i, int j, int[][] array1, Object o, Object[][] array2) { } + int m9() { return 0; } + int[][] mA() { return null; } + Object[][] mB() { return null; } } |