diff options
| -rw-r--r-- | runtime/art_method.cc | 17 | ||||
| -rw-r--r-- | runtime/art_method.h | 4 | ||||
| -rw-r--r-- | runtime/dex/art_dex_file_loader_test.cc | 209 | ||||
| -rw-r--r-- | test/999-redefine-hiddenapi/api-blacklist.txt | 2 | ||||
| -rw-r--r-- | test/999-redefine-hiddenapi/build | 17 | ||||
| -rw-r--r-- | test/999-redefine-hiddenapi/expected.txt | 1 | ||||
| -rw-r--r-- | test/999-redefine-hiddenapi/info.txt | 1 | ||||
| -rwxr-xr-x | test/999-redefine-hiddenapi/run | 17 | ||||
| -rw-r--r-- | test/999-redefine-hiddenapi/src-ex/Test999.java | 25 | ||||
| -rw-r--r-- | test/999-redefine-hiddenapi/src-redefine/art/Test999.java | 25 | ||||
| -rwxr-xr-x | test/999-redefine-hiddenapi/src-redefine/gen.sh | 30 | ||||
| -rw-r--r-- | test/999-redefine-hiddenapi/src/Main.java | 111 | ||||
| -rw-r--r-- | test/999-redefine-hiddenapi/src/art/Redefinition.java | 91 | ||||
| -rw-r--r-- | test/Android.bp | 36 | ||||
| -rwxr-xr-x | test/etc/default-build | 5 | ||||
| -rw-r--r-- | test/knownfailures.json | 11 | ||||
| -rw-r--r-- | test/ti-stress/stress.cc | 5 | ||||
| -rw-r--r-- | tools/ti-fast/Android.bp | 56 | ||||
| -rw-r--r-- | tools/ti-fast/README.md | 101 | ||||
| -rw-r--r-- | tools/ti-fast/tifast.cc | 205 |
20 files changed, 867 insertions, 102 deletions
diff --git a/runtime/art_method.cc b/runtime/art_method.cc index 87fcb20698..608e33cf65 100644 --- a/runtime/art_method.cc +++ b/runtime/art_method.cc @@ -710,6 +710,23 @@ bool ArtMethod::HasAnyCompiledCode() { return GetOatMethodQuickCode(runtime->GetClassLinker()->GetImagePointerSize()) != nullptr; } +void ArtMethod::SetNotIntrinsic() { + if (!IsIntrinsic()) { + return; + } + + // Query the hidden API access flags of the intrinsic. + HiddenApiAccessFlags::ApiList intrinsic_api_list = GetHiddenApiAccessFlags(); + + // Clear intrinsic-related access flags. + ClearAccessFlags(kAccIntrinsic | kAccIntrinsicBits); + + // Re-apply hidden API access flags now that the method is not an intrinsic. + SetAccessFlags(HiddenApiAccessFlags::EncodeForRuntime(GetAccessFlags(), intrinsic_api_list)); + DCHECK_EQ(GetHiddenApiAccessFlags(), intrinsic_api_list); +} + + void ArtMethod::CopyFrom(ArtMethod* src, PointerSize image_pointer_size) { memcpy(reinterpret_cast<void*>(this), reinterpret_cast<const void*>(src), Size(image_pointer_size)); diff --git a/runtime/art_method.h b/runtime/art_method.h index acaa4a68a1..012d706756 100644 --- a/runtime/art_method.h +++ b/runtime/art_method.h @@ -194,9 +194,7 @@ class ArtMethod FINAL { return (GetAccessFlags() & kAccIntrinsicBits) >> kAccFlagsShift; } - void SetNotIntrinsic() REQUIRES_SHARED(Locks::mutator_lock_) { - ClearAccessFlags(kAccIntrinsic | kAccIntrinsicBits); - } + void SetNotIntrinsic() REQUIRES_SHARED(Locks::mutator_lock_); bool IsCopied() { static_assert((kAccCopied & (kAccIntrinsic | kAccIntrinsicBits)) == 0, diff --git a/runtime/dex/art_dex_file_loader_test.cc b/runtime/dex/art_dex_file_loader_test.cc index c0090e62f5..d353c26b35 100644 --- a/runtime/dex/art_dex_file_loader_test.cc +++ b/runtime/dex/art_dex_file_loader_test.cc @@ -43,48 +43,7 @@ static void Copy(const std::string& src, const std::string& dst) { dst_stream << src_stream.rdbuf(); } -class ArtDexFileLoaderTest : public CommonRuntimeTest { - public: - virtual void SetUp() { - CommonRuntimeTest::SetUp(); - - std::string dex_location = GetTestDexFileName("Main"); - std::string multidex_location = GetTestDexFileName("MultiDex"); - - data_location_path_ = android_data_ + "/foo.jar"; - system_location_path_ = GetAndroidRoot() + "/foo.jar"; - system_framework_location_path_ = GetAndroidRoot() + "/framework/foo.jar"; - data_multi_location_path_ = android_data_ + "/multifoo.jar"; - system_multi_location_path_ = GetAndroidRoot() + "/multifoo.jar"; - system_framework_multi_location_path_ = GetAndroidRoot() + "/framework/multifoo.jar"; - - Copy(dex_location, data_location_path_); - Copy(dex_location, system_location_path_); - Copy(dex_location, system_framework_location_path_); - - Copy(multidex_location, data_multi_location_path_); - Copy(multidex_location, system_multi_location_path_); - Copy(multidex_location, system_framework_multi_location_path_); - } - - virtual void TearDown() { - remove(data_location_path_.c_str()); - remove(system_location_path_.c_str()); - remove(system_framework_location_path_.c_str()); - remove(data_multi_location_path_.c_str()); - remove(system_multi_location_path_.c_str()); - remove(system_framework_multi_location_path_.c_str()); - CommonRuntimeTest::TearDown(); - } - - protected: - std::string data_location_path_; - std::string system_location_path_; - std::string system_framework_location_path_; - std::string data_multi_location_path_; - std::string system_multi_location_path_; - std::string system_framework_multi_location_path_; -}; +class ArtDexFileLoaderTest : public CommonRuntimeTest {}; // TODO: Port OpenTestDexFile(s) need to be ported to use non-ART utilities, and // the tests that depend upon them should be moved to dex_file_loader_test.cc @@ -353,21 +312,24 @@ TEST_F(ArtDexFileLoaderTest, GetDexCanonicalLocation) { ASSERT_EQ(0, unlink(dex_location_sym.c_str())); } -TEST_F(ArtDexFileLoaderTest, IsPlatformDexFile) { +TEST_F(ArtDexFileLoaderTest, IsPlatformDexFile_DataDir) { + // Load file from a non-system directory and check that it is not flagged as framework. + std::string data_location_path = android_data_ + "/foo.jar"; + ASSERT_FALSE(LocationIsOnSystemFramework(data_location_path.c_str())); + + Copy(GetTestDexFileName("Main"), data_location_path); + ArtDexFileLoader loader; - bool success; - std::string error_msg; std::vector<std::unique_ptr<const DexFile>> dex_files; - - // Load file from a non-system directory and check that it is not flagged as framework. - ASSERT_FALSE(LocationIsOnSystemFramework(data_location_path_.c_str())); - success = loader.Open(data_location_path_.c_str(), - data_location_path_, - /* verify */ false, - /* verify_checksum */ false, - &error_msg, - &dex_files); + std::string error_msg; + bool success = loader.Open(data_location_path.c_str(), + data_location_path, + /* verify */ false, + /* verify_checksum */ false, + &error_msg, + &dex_files); ASSERT_TRUE(success) << error_msg; + ASSERT_GE(dex_files.size(), 1u); for (std::unique_ptr<const DexFile>& dex_file : dex_files) { ASSERT_FALSE(dex_file->IsPlatformDexFile()); @@ -375,15 +337,27 @@ TEST_F(ArtDexFileLoaderTest, IsPlatformDexFile) { dex_files.clear(); + ASSERT_EQ(0, remove(data_location_path.c_str())); +} + +TEST_F(ArtDexFileLoaderTest, IsPlatformDexFile_SystemDir) { // Load file from a system, non-framework directory and check that it is not flagged as framework. - ASSERT_FALSE(LocationIsOnSystemFramework(system_location_path_.c_str())); - success = loader.Open(system_location_path_.c_str(), - system_location_path_, - /* verify */ false, - /* verify_checksum */ false, - &error_msg, - &dex_files); - ASSERT_TRUE(success); + std::string system_location_path = GetAndroidRoot() + "/foo.jar"; + ASSERT_FALSE(LocationIsOnSystemFramework(system_location_path.c_str())); + + Copy(GetTestDexFileName("Main"), system_location_path); + + ArtDexFileLoader loader; + std::vector<std::unique_ptr<const DexFile>> dex_files; + std::string error_msg; + bool success = loader.Open(system_location_path.c_str(), + system_location_path, + /* verify */ false, + /* verify_checksum */ false, + &error_msg, + &dex_files); + ASSERT_TRUE(success) << error_msg; + ASSERT_GE(dex_files.size(), 1u); for (std::unique_ptr<const DexFile>& dex_file : dex_files) { ASSERT_FALSE(dex_file->IsPlatformDexFile()); @@ -391,15 +365,27 @@ TEST_F(ArtDexFileLoaderTest, IsPlatformDexFile) { dex_files.clear(); + ASSERT_EQ(0, remove(system_location_path.c_str())); +} + +TEST_F(ArtDexFileLoaderTest, IsPlatformDexFile_SystemFrameworkDir) { // Load file from a system/framework directory and check that it is flagged as a framework dex. - ASSERT_TRUE(LocationIsOnSystemFramework(system_framework_location_path_.c_str())); - success = loader.Open(system_framework_location_path_.c_str(), - system_framework_location_path_, - /* verify */ false, - /* verify_checksum */ false, - &error_msg, - &dex_files); - ASSERT_TRUE(success); + std::string system_framework_location_path = GetAndroidRoot() + "/framework/foo.jar"; + ASSERT_TRUE(LocationIsOnSystemFramework(system_framework_location_path.c_str())); + + Copy(GetTestDexFileName("Main"), system_framework_location_path); + + ArtDexFileLoader loader; + std::vector<std::unique_ptr<const DexFile>> dex_files; + std::string error_msg; + bool success = loader.Open(system_framework_location_path.c_str(), + system_framework_location_path, + /* verify */ false, + /* verify_checksum */ false, + &error_msg, + &dex_files); + ASSERT_TRUE(success) << error_msg; + ASSERT_GE(dex_files.size(), 1u); for (std::unique_ptr<const DexFile>& dex_file : dex_files) { ASSERT_TRUE(dex_file->IsPlatformDexFile()); @@ -407,14 +393,27 @@ TEST_F(ArtDexFileLoaderTest, IsPlatformDexFile) { dex_files.clear(); + ASSERT_EQ(0, remove(system_framework_location_path.c_str())); +} + +TEST_F(ArtDexFileLoaderTest, IsPlatformDexFile_DataDir_MultiDex) { // Load multidex file from a non-system directory and check that it is not flagged as framework. - success = loader.Open(data_multi_location_path_.c_str(), - data_multi_location_path_, - /* verify */ false, - /* verify_checksum */ false, - &error_msg, - &dex_files); + std::string data_multi_location_path = android_data_ + "/multifoo.jar"; + ASSERT_FALSE(LocationIsOnSystemFramework(data_multi_location_path.c_str())); + + Copy(GetTestDexFileName("MultiDex"), data_multi_location_path); + + ArtDexFileLoader loader; + std::vector<std::unique_ptr<const DexFile>> dex_files; + std::string error_msg; + bool success = loader.Open(data_multi_location_path.c_str(), + data_multi_location_path, + /* verify */ false, + /* verify_checksum */ false, + &error_msg, + &dex_files); ASSERT_TRUE(success) << error_msg; + ASSERT_GT(dex_files.size(), 1u); for (std::unique_ptr<const DexFile>& dex_file : dex_files) { ASSERT_FALSE(dex_file->IsPlatformDexFile()); @@ -422,15 +421,28 @@ TEST_F(ArtDexFileLoaderTest, IsPlatformDexFile) { dex_files.clear(); + ASSERT_EQ(0, remove(data_multi_location_path.c_str())); +} + +TEST_F(ArtDexFileLoaderTest, IsPlatformDexFile_SystemDir_MultiDex) { // Load multidex file from a system, non-framework directory and check that it is not flagged // as framework. - success = loader.Open(system_multi_location_path_.c_str(), - system_multi_location_path_, - /* verify */ false, - /* verify_checksum */ false, - &error_msg, - &dex_files); - ASSERT_TRUE(success); + std::string system_multi_location_path = GetAndroidRoot() + "/multifoo.jar"; + ASSERT_FALSE(LocationIsOnSystemFramework(system_multi_location_path.c_str())); + + Copy(GetTestDexFileName("MultiDex"), system_multi_location_path); + + ArtDexFileLoader loader; + std::vector<std::unique_ptr<const DexFile>> dex_files; + std::string error_msg; + bool success = loader.Open(system_multi_location_path.c_str(), + system_multi_location_path, + /* verify */ false, + /* verify_checksum */ false, + &error_msg, + &dex_files); + ASSERT_TRUE(success) << error_msg; + ASSERT_GT(dex_files.size(), 1u); for (std::unique_ptr<const DexFile>& dex_file : dex_files) { ASSERT_FALSE(dex_file->IsPlatformDexFile()); @@ -438,19 +450,36 @@ TEST_F(ArtDexFileLoaderTest, IsPlatformDexFile) { dex_files.clear(); + ASSERT_EQ(0, remove(system_multi_location_path.c_str())); +} + +TEST_F(ArtDexFileLoaderTest, IsPlatformDexFile_SystemFrameworkDir_MultiDex) { // Load multidex file from a system/framework directory and check that it is flagged as a // framework dex. - success = loader.Open(system_framework_multi_location_path_.c_str(), - system_framework_multi_location_path_, - /* verify */ false, - /* verify_checksum */ false, - &error_msg, - &dex_files); - ASSERT_TRUE(success); + std::string system_framework_multi_location_path = GetAndroidRoot() + "/framework/multifoo.jar"; + ASSERT_TRUE(LocationIsOnSystemFramework(system_framework_multi_location_path.c_str())); + + Copy(GetTestDexFileName("MultiDex"), system_framework_multi_location_path); + + ArtDexFileLoader loader; + std::vector<std::unique_ptr<const DexFile>> dex_files; + std::string error_msg; + bool success = loader.Open(system_framework_multi_location_path.c_str(), + system_framework_multi_location_path, + /* verify */ false, + /* verify_checksum */ false, + &error_msg, + &dex_files); + ASSERT_TRUE(success) << error_msg; + ASSERT_GT(dex_files.size(), 1u); for (std::unique_ptr<const DexFile>& dex_file : dex_files) { ASSERT_TRUE(dex_file->IsPlatformDexFile()); } + + dex_files.clear(); + + ASSERT_EQ(0, remove(system_framework_multi_location_path.c_str())); } } // namespace art diff --git a/test/999-redefine-hiddenapi/api-blacklist.txt b/test/999-redefine-hiddenapi/api-blacklist.txt new file mode 100644 index 0000000000..63e37aa757 --- /dev/null +++ b/test/999-redefine-hiddenapi/api-blacklist.txt @@ -0,0 +1,2 @@ +Lart/Test999;->foo()V +Lart/Test999;->bar:I diff --git a/test/999-redefine-hiddenapi/build b/test/999-redefine-hiddenapi/build new file mode 100644 index 0000000000..f4b029fb82 --- /dev/null +++ b/test/999-redefine-hiddenapi/build @@ -0,0 +1,17 @@ +#!/bin/bash +# +# Copyright 2018 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +USE_HIDDENAPI=true ./default-build "$@" diff --git a/test/999-redefine-hiddenapi/expected.txt b/test/999-redefine-hiddenapi/expected.txt new file mode 100644 index 0000000000..6a5618ebc6 --- /dev/null +++ b/test/999-redefine-hiddenapi/expected.txt @@ -0,0 +1 @@ +JNI_OnLoad called diff --git a/test/999-redefine-hiddenapi/info.txt b/test/999-redefine-hiddenapi/info.txt new file mode 100644 index 0000000000..87bc30cf80 --- /dev/null +++ b/test/999-redefine-hiddenapi/info.txt @@ -0,0 +1 @@ +Tests that JVMTI class redefinition does not strip away hidden API access flags. diff --git a/test/999-redefine-hiddenapi/run b/test/999-redefine-hiddenapi/run new file mode 100755 index 0000000000..c6e62ae6cd --- /dev/null +++ b/test/999-redefine-hiddenapi/run @@ -0,0 +1,17 @@ +#!/bin/bash +# +# Copyright 2016 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +./default-run "$@" --jvmti diff --git a/test/999-redefine-hiddenapi/src-ex/Test999.java b/test/999-redefine-hiddenapi/src-ex/Test999.java new file mode 100644 index 0000000000..97495c5a47 --- /dev/null +++ b/test/999-redefine-hiddenapi/src-ex/Test999.java @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package art; + +public class Test999 { + public void foo() { + System.out.println("hello"); + } + + public int bar = 42; +} diff --git a/test/999-redefine-hiddenapi/src-redefine/art/Test999.java b/test/999-redefine-hiddenapi/src-redefine/art/Test999.java new file mode 100644 index 0000000000..c1b838ccc7 --- /dev/null +++ b/test/999-redefine-hiddenapi/src-redefine/art/Test999.java @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package art; + +public class Test999 { + public void foo() { + System.out.println("Goodbye"); + } + + public int bar = 64; +} diff --git a/test/999-redefine-hiddenapi/src-redefine/gen.sh b/test/999-redefine-hiddenapi/src-redefine/gen.sh new file mode 100755 index 0000000000..6948cbbfc3 --- /dev/null +++ b/test/999-redefine-hiddenapi/src-redefine/gen.sh @@ -0,0 +1,30 @@ +#!/bin/bash +# +# Copyright 2018 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +set -e +DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +TMP=`mktemp -d` + +CLASS "art/Test999" + +(cd "$TMP" && javac -d "${TMP}" "$DIR/${CLASS}.java" && d8 --output . "$TMP/${CLASS}.class") + +echo ' private static final byte[] CLASS_BYTES = Base64.getDecoder().decode(' +base64 "${TMP}/${CLASS}.class" | sed -E 's/^/ "/' | sed ':a;N;$!ba;s/\n/" +\n/g' | sed -E '$ s/$/");/' +echo ' private static final byte[] DEX_BYTES = Base64.getDecoder().decode(' +base64 "${TMP}/classes.dex" | sed -E 's/^/ "/' | sed ':a;N;$!ba;s/\n/" +\n/g' | sed -E '$ s/$/");/' + +rm -rf "$TMP" diff --git a/test/999-redefine-hiddenapi/src/Main.java b/test/999-redefine-hiddenapi/src/Main.java new file mode 100644 index 0000000000..c6365ac234 --- /dev/null +++ b/test/999-redefine-hiddenapi/src/Main.java @@ -0,0 +1,111 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import java.io.File; +import java.lang.reflect.Method; +import java.util.Base64; + +public class Main { + public static void main(String[] args) throws Exception { + System.loadLibrary(args[0]); + + // Run the initialization routine. This will enable hidden API checks in + // the runtime, in case they are not enabled by default. + init(); + + // Load the '-ex' APK and attach it to the boot class path. + appendToBootClassLoader(DEX_EXTRA); + + // Find the test class in boot class loader and verify that its members are hidden. + Class<?> klass = Class.forName("art.Test999", true, BOOT_CLASS_LOADER); + assertMethodIsHidden(klass, "before redefinition"); + assertFieldIsHidden(klass, "before redefinition"); + + // Redefine the class using JVMTI. + art.Redefinition.setTestConfiguration(art.Redefinition.Config.COMMON_REDEFINE); + art.Redefinition.doCommonClassRedefinition(klass, CLASS_BYTES, DEX_BYTES); + + // Verify that the class members are still hidden. + assertMethodIsHidden(klass, "after redefinition"); + assertFieldIsHidden(klass, "after redefinition"); + } + + private static void assertMethodIsHidden(Class<?> klass, String msg) throws Exception { + try { + klass.getDeclaredMethod("foo"); + // Unexpected. Should have thrown NoSuchMethodException. + throw new Exception("Method should not be accessible " + msg); + } catch (NoSuchMethodException ex) { + // Expected. + } + } + + private static void assertFieldIsHidden(Class<?> klass, String msg) throws Exception { + try { + klass.getDeclaredField("bar"); + // Unexpected. Should have thrown NoSuchFieldException. + throw new Exception("Field should not be accessible " + msg); + } catch (NoSuchFieldException ex) { + // Expected. + } + } + + private static final String DEX_EXTRA = + new File(System.getenv("DEX_LOCATION"), "999-redefine-hiddenapi-ex.jar").getAbsolutePath(); + + private static ClassLoader BOOT_CLASS_LOADER = Object.class.getClassLoader(); + + // Native functions. Note that these are implemented in 674-hiddenapi/hiddenapi.cc. + private static native void appendToBootClassLoader(String dexPath); + private static native void init(); + + /** + * base64 encoded class/dex file for + * + * public class Test999 { + * public void foo() { + * System.out.println("Goodbye"); + * } + * + * public int bar = 64; + * } + */ + private static final byte[] CLASS_BYTES = Base64.getDecoder().decode( + "yv66vgAAADUAIAoABwARCQAGABIJABMAFAgAFQoAFgAXBwAYBwAZAQADYmFyAQABSQEABjxpbml0" + + "PgEAAygpVgEABENvZGUBAA9MaW5lTnVtYmVyVGFibGUBAANmb28BAApTb3VyY2VGaWxlAQAMVGVz" + + "dDk5OS5qYXZhDAAKAAsMAAgACQcAGgwAGwAcAQAHR29vZGJ5ZQcAHQwAHgAfAQALYXJ0L1Rlc3Q5" + + "OTkBABBqYXZhL2xhbmcvT2JqZWN0AQAQamF2YS9sYW5nL1N5c3RlbQEAA291dAEAFUxqYXZhL2lv" + + "L1ByaW50U3RyZWFtOwEAE2phdmEvaW8vUHJpbnRTdHJlYW0BAAdwcmludGxuAQAVKExqYXZhL2xh" + + "bmcvU3RyaW5nOylWACEABgAHAAAAAQABAAgACQAAAAIAAQAKAAsAAQAMAAAAJwACAAEAAAALKrcA" + + "ASoQQLUAArEAAAABAA0AAAAKAAIAAAATAAQAGAABAA4ACwABAAwAAAAlAAIAAQAAAAmyAAMSBLYA" + + "BbEAAAABAA0AAAAKAAIAAAAVAAgAFgABAA8AAAACABA="); + private static final byte[] DEX_BYTES = Base64.getDecoder().decode( + "ZGV4CjAzNQD0dZ+IWxOi+cJDSWjfTnUerlZj1Lll3ONIAwAAcAAAAHhWNBIAAAAAAAAAAJwCAAAQ" + + "AAAAcAAAAAcAAACwAAAAAgAAAMwAAAACAAAA5AAAAAQAAAD0AAAAAQAAABQBAAAUAgAANAEAAIYB" + + "AACOAQAAlwEAAJoBAACpAQAAwAEAANQBAADoAQAA/AEAAAoCAAANAgAAEQIAABYCAAAbAgAAIAIA" + + "ACkCAAACAAAAAwAAAAQAAAAFAAAABgAAAAcAAAAJAAAACQAAAAYAAAAAAAAACgAAAAYAAACAAQAA" + + "AQAAAAsAAAAFAAIADQAAAAEAAAAAAAAAAQAAAAwAAAACAAEADgAAAAMAAAAAAAAAAQAAAAEAAAAD" + + "AAAAAAAAAAgAAAAAAAAAhwIAAAAAAAACAAEAAQAAAHQBAAAIAAAAcBADAAEAEwBAAFkQAAAOAAMA" + + "AQACAAAAeQEAAAgAAABiAAEAGgEBAG4gAgAQAA4AEwAOQAAVAA54AAAAAQAAAAQABjxpbml0PgAH" + + "R29vZGJ5ZQABSQANTGFydC9UZXN0OTk5OwAVTGphdmEvaW8vUHJpbnRTdHJlYW07ABJMamF2YS9s" + + "YW5nL09iamVjdDsAEkxqYXZhL2xhbmcvU3RyaW5nOwASTGphdmEvbGFuZy9TeXN0ZW07AAxUZXN0" + + "OTk5LmphdmEAAVYAAlZMAANiYXIAA2ZvbwADb3V0AAdwcmludGxuAFx+fkQ4eyJtaW4tYXBpIjox" + + "LCJzaGEtMSI6IjU2YzJlMzBmNTIzM2I4NDRmZjZkZGQ4N2ZiNTNkMzRmYjE3MjM3ZGYiLCJ2ZXJz" + + "aW9uIjoidjEuMi4xNS1kZXYifQAAAQEBAAEAgYAEtAIBAdQCAAAAAAAOAAAAAAAAAAEAAAAAAAAA" + + "AQAAABAAAABwAAAAAgAAAAcAAACwAAAAAwAAAAIAAADMAAAABAAAAAIAAADkAAAABQAAAAQAAAD0" + + "AAAABgAAAAEAAAAUAQAAASAAAAIAAAA0AQAAAyAAAAIAAAB0AQAAARAAAAEAAACAAQAAAiAAABAA" + + "AACGAQAAACAAAAEAAACHAgAAAxAAAAEAAACYAgAAABAAAAEAAACcAgAA"); +} diff --git a/test/999-redefine-hiddenapi/src/art/Redefinition.java b/test/999-redefine-hiddenapi/src/art/Redefinition.java new file mode 100644 index 0000000000..1eec70b48c --- /dev/null +++ b/test/999-redefine-hiddenapi/src/art/Redefinition.java @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package art; + +import java.util.ArrayList; +// Common Redefinition functions. Placed here for use by CTS +public class Redefinition { + public static final class CommonClassDefinition { + public final Class<?> target; + public final byte[] class_file_bytes; + public final byte[] dex_file_bytes; + + public CommonClassDefinition(Class<?> target, byte[] class_file_bytes, byte[] dex_file_bytes) { + this.target = target; + this.class_file_bytes = class_file_bytes; + this.dex_file_bytes = dex_file_bytes; + } + } + + // A set of possible test configurations. Test should set this if they need to. + // This must be kept in sync with the defines in ti-agent/common_helper.cc + public static enum Config { + COMMON_REDEFINE(0), + COMMON_RETRANSFORM(1), + COMMON_TRANSFORM(2); + + private final int val; + private Config(int val) { + this.val = val; + } + } + + public static void setTestConfiguration(Config type) { + nativeSetTestConfiguration(type.val); + } + + private static native void nativeSetTestConfiguration(int type); + + // Transforms the class + public static native void doCommonClassRedefinition(Class<?> target, + byte[] classfile, + byte[] dexfile); + + public static void doMultiClassRedefinition(CommonClassDefinition... defs) { + ArrayList<Class<?>> classes = new ArrayList<>(); + ArrayList<byte[]> class_files = new ArrayList<>(); + ArrayList<byte[]> dex_files = new ArrayList<>(); + + for (CommonClassDefinition d : defs) { + classes.add(d.target); + class_files.add(d.class_file_bytes); + dex_files.add(d.dex_file_bytes); + } + doCommonMultiClassRedefinition(classes.toArray(new Class<?>[0]), + class_files.toArray(new byte[0][]), + dex_files.toArray(new byte[0][])); + } + + public static void addMultiTransformationResults(CommonClassDefinition... defs) { + for (CommonClassDefinition d : defs) { + addCommonTransformationResult(d.target.getCanonicalName(), + d.class_file_bytes, + d.dex_file_bytes); + } + } + + public static native void doCommonMultiClassRedefinition(Class<?>[] targets, + byte[][] classfiles, + byte[][] dexfiles); + public static native void doCommonClassRetransformation(Class<?>... target); + public static native void setPopRetransformations(boolean pop); + public static native void popTransformationFor(String name); + public static native void enableCommonRetransformation(boolean enable); + public static native void addCommonTransformationResult(String target_name, + byte[] class_bytes, + byte[] dex_bytes); +} diff --git a/test/Android.bp b/test/Android.bp index 76189f62a9..84f2e224d5 100644 --- a/test/Android.bp +++ b/test/Android.bp @@ -370,22 +370,27 @@ cc_library_static { } art_cc_defaults { - name: "libtistress-defaults", + name: "libtistress-srcs", defaults: ["libartagent-defaults"], srcs: [ "ti-stress/stress.cc", ], + header_libs: ["libopenjdkjvmti_headers"], +} + +art_cc_defaults { + name: "libtistress-defaults", + defaults: ["libtistress-srcs"], shared_libs: [ "libbase", "slicer", ], - header_libs: ["libopenjdkjvmti_headers"], } art_cc_test_library { name: "libtistress", defaults: ["libtistress-defaults"], - shared_libs: ["libart"], + shared_libs: ["libartbase"], } art_cc_test_library { @@ -394,7 +399,30 @@ art_cc_test_library { "art_debug_defaults", "libtistress-defaults", ], - shared_libs: ["libartd"], + shared_libs: ["libartbased"], +} + +art_cc_defaults { + name: "libtistress-static-defaults", + defaults: ["libtistress-srcs"], + static_libs: art_static_dependencies + [ + "slicer", + ], +} + +art_cc_test_library { + name: "libtistresss", + defaults: ["libtistress-static-defaults"], + static_libs: ["libartbase"], +} + +art_cc_test_library { + name: "libtistressds", + defaults: [ + "art_debug_defaults", + "libtistress-static-defaults" + ], + static_libs: ["libartbased"], } cc_defaults { diff --git a/test/etc/default-build b/test/etc/default-build index 8bb898c7c1..c61de0ab6e 100755 --- a/test/etc/default-build +++ b/test/etc/default-build @@ -561,6 +561,11 @@ fi if [[ -d classes-ex ]] && [ ${NEED_DEX} = "true" ]; then make_dex classes-ex + # Apply hiddenapi on the dex files if the test has API list file(s). + if [ ${USE_HIDDENAPI} = "true" -a ${HAS_HIDDENAPI_SPEC} = "true" ]; then + make_hiddenapi classes-ex.dex + fi + # quick shuffle so that the stored name is "classes.dex" mv classes.dex classes-1.dex mv classes-ex.dex classes.dex diff --git a/test/knownfailures.json b/test/knownfailures.json index f473a99a27..a202044786 100644 --- a/test/knownfailures.json +++ b/test/knownfailures.json @@ -454,7 +454,8 @@ "674-hiddenapi", "649-vdex-duplicate-method", "804-class-extends-itself", - "921-hello-failure" + "921-hello-failure", + "999-redefine-hiddenapi" ], "description": [ "Tests that use illegal dex files or otherwise break dexter assumptions" @@ -471,7 +472,8 @@ "629-vdex-speed", "647-jni-get-field-id", "674-hiddenapi", - "944-transform-classloaders" + "944-transform-classloaders", + "999-redefine-hiddenapi" ], "description": [ "Tests that use custom class loaders or other features not supported ", @@ -876,7 +878,6 @@ "667-jit-jni-stub", "667-out-of-bounds", "668-aiobe", - "674-hiddenapi", "674-hotness-compiled", "674-vdex-uncompress", "675-checker-unverified-method", @@ -954,8 +955,10 @@ }, { "tests": ["616-cha-unloading", + "674-hiddenapi", "678-quickening", - "679-locks"], + "679-locks", + "999-redefine-hiddenapi"], "variant": "jvm", "description": ["Doesn't run on RI."] }, diff --git a/test/ti-stress/stress.cc b/test/ti-stress/stress.cc index bbe74656dd..0eba7426c0 100644 --- a/test/ti-stress/stress.cc +++ b/test/ti-stress/stress.cc @@ -25,7 +25,6 @@ #include <jni.h> #include "base/utils.h" -#include "exec_utils.h" #include "jvmti.h" #pragma clang diagnostic push @@ -920,4 +919,8 @@ extern "C" JNIEXPORT jint JNICALL Agent_OnLoad(JavaVM* vm, return 0; } +extern "C" JNIEXPORT jint JNICALL Agent_OnAttach(JavaVM* vm, char* options, void* reserved) { + return Agent_OnLoad(vm, options, reserved); +} + } // namespace art diff --git a/tools/ti-fast/Android.bp b/tools/ti-fast/Android.bp new file mode 100644 index 0000000000..fd867c9bcc --- /dev/null +++ b/tools/ti-fast/Android.bp @@ -0,0 +1,56 @@ +// +// Copyright (C) 2018 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +// Build variants {target,host} x {debug,ndebug} x {32,64} +cc_defaults { + name: "tifast-defaults", + host_supported: true, + srcs: ["tifast.cc"], + defaults: ["art_defaults"], + + // Note that this tool needs to be built for both 32-bit and 64-bit since it requires + // to be same ISA as what it is attached to. + compile_multilib: "both", + + shared_libs: [ + "libbase", + ], + header_libs: [ + "libopenjdkjvmti_headers", + ], + multilib: { + lib32: { + suffix: "32", + }, + lib64: { + suffix: "64", + }, + }, + symlink_preferred_arch: true, +} + +art_cc_library { + name: "libtifast", + defaults: ["tifast-defaults"], +} + +art_cc_library { + name: "libtifastd", + defaults: [ + "art_debug_defaults", + "tifast-defaults", + ], +} diff --git a/tools/ti-fast/README.md b/tools/ti-fast/README.md new file mode 100644 index 0000000000..bc468826ec --- /dev/null +++ b/tools/ti-fast/README.md @@ -0,0 +1,101 @@ +# tifast + +tifast is a JVMTI agent designed for profiling the performance impact listening +to various JVMTI events. It is called tifast since none of the event handlers do +anything meaning that it can be considered speed-of-light. + +# Usage +### Build +> `make libtifast` + +The libraries will be built for 32-bit, 64-bit, host and target. Below examples +assume you want to use the 64-bit version. + +### Command Line + +The agent is loaded using -agentpath like normal. It takes arguments in the +following format: +> `[log,][EventName1[,EventName2[,...]]]` + +* If 'log' is the first argument the event handlers will LOG(INFO) when they are + called. This behavior is static. The no-log methods have no branches and just + immediately return. + +* The event-names are the same names as are used in the jvmtiEventCallbacks + struct. + +* All required capabilities are automatically gained. No capabilities other than + those needed to listen for the events are gained. + +* Only events which do not require additional function calls to cause delivery + and are sent more than once are supported. + +#### Supported events + +The following events may be listened for with this agent + +* `SingleStep` + +* `MethodEntry` + +* `MethodExit` + +* `NativeMethodBind` + +* `Exception` + +* `ExceptionCatch` + +* `ThreadStart` + +* `ThreadEnd` + +* `ClassLoad` + +* `ClassPrepare` + +* `ClassFileLoadHook` + +* `CompiledMethodLoad` + +* `CompiledMethodUnload` + +* `DynamicCodeGenerated` + +* `DataDumpRequest` + +* `MonitorContendedEnter` + +* `MonitorContendedEntered` + +* `MonitorWait` + +* `MonitorWaited` + +* `ResourceExhausted` + +* `VMObjectAlloc` + +* `GarbageCollectionStart` + +* `GarbageCollectionFinish` + +All other events cannot be listened for by this agent. Most of these missing +events either require the use of other functions in order to be called +(`FramePop`, `ObjectFree`, etc) or are only called once (`VMInit`, `VMDeath`, +etc). + +#### ART +> `art -Xplugin:$ANDROID_HOST_OUT/lib64/libopenjdkjvmti.so '-agentpath:libtifast.so=MethodEntry' -cp tmp/java/helloworld.dex -Xint helloworld` + +* `-Xplugin` and `-agentpath` need to be used, otherwise the agent will fail during init. +* If using `libartd.so`, make sure to use the debug version of jvmti. + +> `adb shell setenforce 0` +> +> `adb push $ANDROID_PRODUCT_OUT/system/lib64/libtifast.so /data/local/tmp/` +> +> `adb shell am start-activity --attach-agent /data/local/tmp/libtifast.so=MonitorWait,ClassPrepare some.debuggable.apps/.the.app.MainActivity` + +#### RI +> `java '-agentpath:libtifast.so=MethodEntry' -cp tmp/helloworld/classes helloworld` diff --git a/tools/ti-fast/tifast.cc b/tools/ti-fast/tifast.cc new file mode 100644 index 0000000000..952fba8720 --- /dev/null +++ b/tools/ti-fast/tifast.cc @@ -0,0 +1,205 @@ +// Copyright (C) 2018 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#include <android-base/logging.h> + +#include <atomic> +#include <iostream> +#include <istream> +#include <iomanip> +#include <jni.h> +#include <jvmti.h> +#include <memory> +#include <string> +#include <sstream> +#include <vector> + +namespace tifast { + +#define EVENT(x) JVMTI_EVENT_ ## x + +namespace { + +static void AddCapsForEvent(jvmtiEvent event, jvmtiCapabilities* caps) { + switch (event) { +#define DO_CASE(name, cap_name) \ + case EVENT(name): \ + caps->cap_name = 1; \ + break + DO_CASE(SINGLE_STEP, can_generate_single_step_events); + DO_CASE(METHOD_ENTRY, can_generate_method_entry_events); + DO_CASE(METHOD_EXIT, can_generate_method_exit_events); + DO_CASE(NATIVE_METHOD_BIND, can_generate_native_method_bind_events); + DO_CASE(EXCEPTION, can_generate_exception_events); + DO_CASE(EXCEPTION_CATCH, can_generate_exception_events); + DO_CASE(COMPILED_METHOD_LOAD, can_generate_compiled_method_load_events); + DO_CASE(COMPILED_METHOD_UNLOAD, can_generate_compiled_method_load_events); + DO_CASE(MONITOR_CONTENDED_ENTER, can_generate_monitor_events); + DO_CASE(MONITOR_CONTENDED_ENTERED, can_generate_monitor_events); + DO_CASE(MONITOR_WAIT, can_generate_monitor_events); + DO_CASE(MONITOR_WAITED, can_generate_monitor_events); + DO_CASE(VM_OBJECT_ALLOC, can_generate_vm_object_alloc_events); + DO_CASE(GARBAGE_COLLECTION_START, can_generate_garbage_collection_events); + DO_CASE(GARBAGE_COLLECTION_FINISH, can_generate_garbage_collection_events); +#undef DO_CASE + default: break; + } +} + +// Setup for all supported events. Give a macro with fun(name, event_num, args) +#define FOR_ALL_SUPPORTED_EVENTS(fun) \ + fun(SingleStep, EVENT(SINGLE_STEP), (jvmtiEnv*, JNIEnv*, jthread, jmethodID, jlocation)) \ + fun(MethodEntry, EVENT(METHOD_ENTRY), (jvmtiEnv*, JNIEnv*, jthread, jmethodID)) \ + fun(MethodExit, EVENT(METHOD_ENTRY), (jvmtiEnv*, JNIEnv*, jthread, jmethodID, jboolean, jvalue)) \ + fun(NativeMethodBind, EVENT(NATIVE_METHOD_BIND), (jvmtiEnv*, JNIEnv*, jthread, jmethodID, void*, void**)) \ + fun(Exception, EVENT(EXCEPTION), (jvmtiEnv*, JNIEnv*, jthread, jmethodID, jlocation, jobject, jmethodID, jlocation)) \ + fun(ExceptionCatch, EVENT(EXCEPTION_CATCH), (jvmtiEnv*, JNIEnv*, jthread, jmethodID, jlocation, jobject)) \ + fun(ThreadStart, EVENT(THREAD_START), (jvmtiEnv*, JNIEnv*, jthread)) \ + fun(ThreadEnd, EVENT(THREAD_END), (jvmtiEnv*, JNIEnv*, jthread)) \ + fun(ClassLoad, EVENT(CLASS_LOAD), (jvmtiEnv*, JNIEnv*, jthread, jclass)) \ + fun(ClassPrepare, EVENT(CLASS_PREPARE), (jvmtiEnv*, JNIEnv*, jthread, jclass)) \ + fun(ClassFileLoadHook, EVENT(CLASS_FILE_LOAD_HOOK), (jvmtiEnv*, JNIEnv*, jclass, jobject, const char*, jobject, jint, const unsigned char*, jint*, unsigned char**)) \ + fun(CompiledMethodLoad, EVENT(COMPILED_METHOD_LOAD), (jvmtiEnv*, jmethodID, jint, const void*, jint, const jvmtiAddrLocationMap*, const void*)) \ + fun(CompiledMethodUnload, EVENT(COMPILED_METHOD_UNLOAD), (jvmtiEnv*, jmethodID, const void*)) \ + fun(DynamicCodeGenerated, EVENT(DYNAMIC_CODE_GENERATED), (jvmtiEnv*, const char*, const void*, jint)) \ + fun(DataDumpRequest, EVENT(DATA_DUMP_REQUEST), (jvmtiEnv*)) \ + fun(MonitorContendedEnter, EVENT(MONITOR_CONTENDED_ENTER), (jvmtiEnv*, JNIEnv*, jthread, jobject)) \ + fun(MonitorContendedEntered, EVENT(MONITOR_CONTENDED_ENTERED), (jvmtiEnv*, JNIEnv*, jthread, jobject)) \ + fun(MonitorWait, EVENT(MONITOR_WAIT), (jvmtiEnv*, JNIEnv*, jthread, jobject, jlong)) \ + fun(MonitorWaited, EVENT(MONITOR_WAITED), (jvmtiEnv*, JNIEnv*, jthread, jobject, jboolean)) \ + fun(ResourceExhausted, EVENT(RESOURCE_EXHAUSTED), (jvmtiEnv*, JNIEnv*, jint, const void*, const char*)) \ + fun(VMObjectAlloc, EVENT(VM_OBJECT_ALLOC), (jvmtiEnv*, JNIEnv*, jthread, jobject, jclass, jlong)) \ + fun(GarbageCollectionStart, EVENT(GARBAGE_COLLECTION_START), (jvmtiEnv*)) \ + fun(GarbageCollectionFinish, EVENT(GARBAGE_COLLECTION_FINISH), (jvmtiEnv*)) + +#define GENERATE_EMPTY_FUNCTION(name, number, args) \ + static void JNICALL empty ## name args { } +FOR_ALL_SUPPORTED_EVENTS(GENERATE_EMPTY_FUNCTION) +#undef GENERATE_EMPTY_FUNCTION + +static jvmtiEventCallbacks kEmptyCallbacks { +#define CREATE_EMPTY_EVENT_CALLBACKS(name, num, args) \ + .name = empty ## name, + FOR_ALL_SUPPORTED_EVENTS(CREATE_EMPTY_EVENT_CALLBACKS) +#undef CREATE_EMPTY_EVENT_CALLBACKS +}; + +#define GENERATE_LOG_FUNCTION(name, number, args) \ + static void JNICALL log ## name args { \ + LOG(INFO) << "Got event " << #name ; \ + } +FOR_ALL_SUPPORTED_EVENTS(GENERATE_LOG_FUNCTION) +#undef GENERATE_LOG_FUNCTION + +static jvmtiEventCallbacks kLogCallbacks { +#define CREATE_LOG_EVENT_CALLBACK(name, num, args) \ + .name = log ## name, + FOR_ALL_SUPPORTED_EVENTS(CREATE_LOG_EVENT_CALLBACK) +#undef CREATE_LOG_EVENT_CALLBACK +}; + +static jvmtiEvent NameToEvent(const std::string& desired_name) { +#define CHECK_NAME(name, event, args) \ + if (desired_name == #name) { \ + return event; \ + } + FOR_ALL_SUPPORTED_EVENTS(CHECK_NAME); + LOG(FATAL) << "Unknown event " << desired_name; + __builtin_unreachable(); +#undef CHECK_NAME +} + +#undef FOR_ALL_SUPPORTED_EVENTS +static std::vector<jvmtiEvent> GetRequestedEventList(const std::string& args) { + std::vector<jvmtiEvent> res; + std::stringstream args_stream(args); + std::string item; + while (std::getline(args_stream, item, ',')) { + if (item == "") { + continue; + } + res.push_back(NameToEvent(item)); + } + return res; +} + +} // namespace + +static jint AgentStart(JavaVM* vm, + char* options, + void* reserved ATTRIBUTE_UNUSED) { + jvmtiEnv* jvmti = nullptr; + jvmtiError error = JVMTI_ERROR_NONE; + { + jint res = 0; + res = vm->GetEnv(reinterpret_cast<void**>(&jvmti), JVMTI_VERSION_1_1); + + if (res != JNI_OK || jvmti == nullptr) { + LOG(ERROR) << "Unable to access JVMTI, error code " << res; + return JNI_ERR; + } + } + std::string args(options); + bool is_log = false; + if (args.compare(0, 3, "log") == 0) { + is_log = true; + args = args.substr(3); + } + + std::vector<jvmtiEvent> events = GetRequestedEventList(args); + + jvmtiCapabilities caps{}; + for (jvmtiEvent e : events) { + AddCapsForEvent(e, &caps); + } + error = jvmti->AddCapabilities(&caps); + if (error != JVMTI_ERROR_NONE) { + LOG(ERROR) << "Unable to set caps"; + return JNI_ERR; + } + + if (is_log) { + error = jvmti->SetEventCallbacks(&kLogCallbacks, static_cast<jint>(sizeof(kLogCallbacks))); + } else { + error = jvmti->SetEventCallbacks(&kEmptyCallbacks, static_cast<jint>(sizeof(kEmptyCallbacks))); + } + if (error != JVMTI_ERROR_NONE) { + LOG(ERROR) << "Unable to set event callbacks."; + return JNI_ERR; + } + for (jvmtiEvent e : events) { + error = jvmti->SetEventNotificationMode(JVMTI_ENABLE, + e, + nullptr /* all threads */); + if (error != JVMTI_ERROR_NONE) { + LOG(ERROR) << "Unable to enable event " << e; + return JNI_ERR; + } + } + return JNI_OK; +} + +// Late attachment (e.g. 'am attach-agent'). +extern "C" JNIEXPORT jint JNICALL Agent_OnAttach(JavaVM *vm, char* options, void* reserved) { + return AgentStart(vm, options, reserved); +} + +// Early attachment +extern "C" JNIEXPORT jint JNICALL Agent_OnLoad(JavaVM* jvm, char* options, void* reserved) { + return AgentStart(jvm, options, reserved); +} + +} // namespace tifast + |