diff options
26 files changed, 388 insertions, 97 deletions
diff --git a/libartbase/base/unix_file/fd_file.cc b/libartbase/base/unix_file/fd_file.cc index f9da178de8..b2881b8ed0 100644 --- a/libartbase/base/unix_file/fd_file.cc +++ b/libartbase/base/unix_file/fd_file.cc @@ -31,7 +31,7 @@ #else #include <algorithm> #include "base/stl_util.h" -#include "globals.h" +#include "base/globals.h" #endif namespace unix_file { diff --git a/openjdkjvmti/ti_redefine.cc b/openjdkjvmti/ti_redefine.cc index 46734548a8..5d430d2073 100644 --- a/openjdkjvmti/ti_redefine.cc +++ b/openjdkjvmti/ti_redefine.cc @@ -1402,13 +1402,6 @@ void Redefiner::ClassRedefinition::UpdateMethods(art::ObjPtr<art::mirror::Class> method.SetCodeItemOffset(dex_file_->FindCodeItemOffset(class_def, dex_method_idx)); // Clear all the intrinsics related flags. method.SetNotIntrinsic(); - // Notify the jit that this method is redefined. - art::jit::Jit* jit = driver_->runtime_->GetJit(); - // Non-invokable methods don't have any JIT data associated with them so we don't need to tell - // the jit about them. - if (jit != nullptr && method.IsInvokable()) { - jit->GetCodeCache()->NotifyMethodRedefined(&method); - } } } @@ -1450,6 +1443,23 @@ void Redefiner::ClassRedefinition::UpdateClass( art::ObjPtr<art::mirror::ClassExt> ext(mclass->GetExtData()); CHECK(!ext.IsNull()); ext->SetOriginalDexFile(original_dex_file); + + // Notify the jit that all the methods in this class were redefined. Need to do this last since + // the jit relies on the dex_file_ being correct (for native methods at least) to find the method + // meta-data. + art::jit::Jit* jit = driver_->runtime_->GetJit(); + if (jit != nullptr) { + art::PointerSize image_pointer_size = + driver_->runtime_->GetClassLinker()->GetImagePointerSize(); + auto code_cache = jit->GetCodeCache(); + // Non-invokable methods don't have any JIT data associated with them so we don't need to tell + // the jit about them. + for (art::ArtMethod& method : mclass->GetDeclaredMethods(image_pointer_size)) { + if (method.IsInvokable()) { + code_cache->NotifyMethodRedefined(&method); + } + } + } } // Restores the old obsolete methods maps if it turns out they weren't needed (ie there were no new diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc index 3dc2cb572e..e8f720bf3a 100644 --- a/runtime/gc/heap.cc +++ b/runtime/gc/heap.cc @@ -143,6 +143,10 @@ static constexpr bool kLogAllGCs = false; static constexpr size_t kPartialTlabSize = 16 * KB; static constexpr bool kUsePartialTlabs = true; +// Use Max heap for 2 seconds, this is smaller than the usual 5s window since we don't want to leave +// allocate with relaxed ergonomics for that long. +static constexpr size_t kPostForkMaxHeapDurationMS = 2000; + #if defined(__LP64__) || !defined(ADDRESS_SANITIZER) // 300 MB (0x12c00000) - (default non-moving space capacity). static uint8_t* const kPreferredAllocSpaceBegin = @@ -3539,6 +3543,12 @@ void Heap::ClampGrowthLimit() { } void Heap::ClearGrowthLimit() { + if (max_allowed_footprint_ == growth_limit_ && growth_limit_ < capacity_) { + max_allowed_footprint_ = capacity_; + concurrent_start_bytes_ = + std::max(max_allowed_footprint_, kMinConcurrentRemainingBytes) - + kMinConcurrentRemainingBytes; + } growth_limit_ = capacity_; ScopedObjectAccess soa(Thread::Current()); for (const auto& space : continuous_spaces_) { @@ -4110,5 +4120,32 @@ void Heap::VlogHeapGrowth(size_t max_allowed_footprint, size_t new_footprint, si << PrettySize(new_footprint) << " for a " << PrettySize(alloc_size) << " allocation"; } +class Heap::TriggerPostForkCCGcTask : public HeapTask { + public: + explicit TriggerPostForkCCGcTask(uint64_t target_time) : HeapTask(target_time) {} + void Run(Thread* self) OVERRIDE { + gc::Heap* heap = Runtime::Current()->GetHeap(); + // Trigger a GC, if not already done. The first GC after fork, whenever + // takes place, will adjust the thresholds to normal levels. + if (heap->max_allowed_footprint_ == heap->growth_limit_) { + heap->RequestConcurrentGC(self, kGcCauseBackground, false); + } + } +}; + +void Heap::PostForkChildAction(Thread* self) { + // Temporarily increase max_allowed_footprint_ and concurrent_start_bytes_ to + // max values to avoid GC during app launch. + if (collector_type_ == kCollectorTypeCC && !IsLowMemoryMode()) { + // Set max_allowed_footprint_ to the largest allowed value. + SetIdealFootprint(growth_limit_); + // Set concurrent_start_bytes_ to half of the heap size. + concurrent_start_bytes_ = std::max(max_allowed_footprint_ / 2, GetBytesAllocated()); + + GetTaskProcessor()->AddTask( + self, new TriggerPostForkCCGcTask(NanoTime() + MsToNs(kPostForkMaxHeapDurationMS))); + } +} + } // namespace gc } // namespace art diff --git a/runtime/gc/heap.h b/runtime/gc/heap.h index 4de03318a0..d9eff7bfef 100644 --- a/runtime/gc/heap.h +++ b/runtime/gc/heap.h @@ -832,10 +832,13 @@ class Heap { const Verification* GetVerification() const; + void PostForkChildAction(Thread* self); + private: class ConcurrentGCTask; class CollectorTransitionTask; class HeapTrimTask; + class TriggerPostForkCCGcTask; // Compact source space to target space. Returns the collector used. collector::GarbageCollector* Compact(space::ContinuousMemMapAllocSpace* target_space, diff --git a/runtime/jit/jit_code_cache.cc b/runtime/jit/jit_code_cache.cc index 51a63ddf8a..0941b0beb3 100644 --- a/runtime/jit/jit_code_cache.cc +++ b/runtime/jit/jit_code_cache.cc @@ -328,33 +328,20 @@ const void* JitCodeCache::GetJniStubCode(ArtMethod* method) { class ScopedCodeCacheWrite : ScopedTrace { public: - explicit ScopedCodeCacheWrite(MemMap* code_map, bool only_for_tlb_shootdown = false) + explicit ScopedCodeCacheWrite(MemMap* code_map) : ScopedTrace("ScopedCodeCacheWrite"), - code_map_(code_map), - only_for_tlb_shootdown_(only_for_tlb_shootdown) { + code_map_(code_map) { ScopedTrace trace("mprotect all"); - CheckedCall(mprotect, - "make code writable", - code_map_->Begin(), - only_for_tlb_shootdown_ ? kPageSize : code_map_->Size(), - kProtAll); + CheckedCall(mprotect, "make code writable", code_map_->Begin(), code_map_->Size(), kProtAll); } ~ScopedCodeCacheWrite() { ScopedTrace trace("mprotect code"); - CheckedCall(mprotect, - "make code protected", - code_map_->Begin(), - only_for_tlb_shootdown_ ? kPageSize : code_map_->Size(), - kProtCode); + CheckedCall(mprotect, "make code protected", code_map_->Begin(), code_map_->Size(), kProtCode); } private: MemMap* const code_map_; - // If we're using ScopedCacheWrite only for TLB shootdown, we limit the scope of mprotect to - // one page. - const bool only_for_tlb_shootdown_; - DISALLOW_COPY_AND_ASSIGN(ScopedCodeCacheWrite); }; @@ -812,8 +799,6 @@ uint8_t* JitCodeCache::CommitCodeInternal(Thread* self, FillRootTable(roots_data, roots); { // Flush data cache, as compiled code references literals in it. - // We also need a TLB shootdown to act as memory barrier across cores. - ScopedCodeCacheWrite ccw(code_map_.get(), /* only_for_tlb_shootdown */ true); FlushDataCache(reinterpret_cast<char*>(roots_data), reinterpret_cast<char*>(roots_data + data_size)); } diff --git a/runtime/native/dalvik_system_ZygoteHooks.cc b/runtime/native/dalvik_system_ZygoteHooks.cc index 08e471b9ec..89135698e3 100644 --- a/runtime/native/dalvik_system_ZygoteHooks.cc +++ b/runtime/native/dalvik_system_ZygoteHooks.cc @@ -306,6 +306,8 @@ static void ZygoteHooks_nativePostForkChild(JNIEnv* env, LOG(ERROR) << StringPrintf("Unknown bits set in runtime_flags: %#x", runtime_flags); } + Runtime::Current()->GetHeap()->PostForkChildAction(thread); + // Update tracing. if (Trace::GetMethodTracingMode() != TracingMode::kTracingInactive) { Trace::TraceOutputMode output_mode = Trace::GetOutputMode(); diff --git a/runtime/oat_file_assistant.cc b/runtime/oat_file_assistant.cc index 1d091e9d71..5888c37582 100644 --- a/runtime/oat_file_assistant.cc +++ b/runtime/oat_file_assistant.cc @@ -1263,10 +1263,14 @@ std::unique_ptr<OatFile> OatFileAssistant::OatFileInfo::ReleaseFileForUse() { switch (Status()) { case kOatBootImageOutOfDate: + // OutOfDate may be either a mismatched image, or a missing image. if (oat_file_assistant_->HasOriginalDexFiles()) { - // If there are original dex files, it is better to use them. + // If there are original dex files, it is better to use them (to avoid a potential + // quickening mismatch because the boot image changed). break; } + // If we do not accept the oat file, we may not have access to dex bytecode at all. Grudgingly + // go forward. FALLTHROUGH_INTENDED; case kOatRelocationOutOfDate: diff --git a/runtime/parsed_options.cc b/runtime/parsed_options.cc index 5518eb2c49..470287b449 100644 --- a/runtime/parsed_options.cc +++ b/runtime/parsed_options.cc @@ -161,10 +161,6 @@ std::unique_ptr<RuntimeParser> ParsedOptions::MakeParser(bool ignore_unrecognize .Define({"-XX:EnableHSpaceCompactForOOM", "-XX:DisableHSpaceCompactForOOM"}) .WithValues({true, false}) .IntoKey(M::EnableHSpaceCompactForOOM) - .Define("-XX:DumpNativeStackOnSigQuit:_") - .WithType<bool>() - .WithValueMap({{"false", false}, {"true", true}}) - .IntoKey(M::DumpNativeStackOnSigQuit) .Define("-XX:MadviseRandomAccess:_") .WithType<bool>() .WithValueMap({{"false", false}, {"true", true}}) @@ -735,7 +731,6 @@ void ParsedOptions::Usage(const char* fmt, ...) { UsageMessage(stream, " -XX:BackgroundGC=none\n"); UsageMessage(stream, " -XX:LargeObjectSpace={disabled,map,freelist}\n"); UsageMessage(stream, " -XX:LargeObjectThreshold=N\n"); - UsageMessage(stream, " -XX:DumpNativeStackOnSigQuit=booleanvalue\n"); UsageMessage(stream, " -XX:MadviseRandomAccess:booleanvalue\n"); UsageMessage(stream, " -XX:SlowDebug={false,true}\n"); UsageMessage(stream, " -Xmethod-trace\n"); diff --git a/runtime/runtime.cc b/runtime/runtime.cc index 8672482a8f..0ca646cdb6 100644 --- a/runtime/runtime.cc +++ b/runtime/runtime.cc @@ -271,7 +271,6 @@ Runtime::Runtime() pending_hidden_api_warning_(false), dedupe_hidden_api_warnings_(true), always_set_hidden_api_warning_flag_(false), - dump_native_stack_on_sig_quit_(true), pruned_dalvik_cache_(false), // Initially assume we perceive jank in case the process state is never updated. process_state_(kProcessStateJankPerceptible), @@ -1154,7 +1153,6 @@ bool Runtime::Init(RuntimeArgumentMap&& runtime_options_in) { is_explicit_gc_disabled_ = runtime_options.Exists(Opt::DisableExplicitGC); dex2oat_enabled_ = runtime_options.GetOrDefault(Opt::Dex2Oat); image_dex2oat_enabled_ = runtime_options.GetOrDefault(Opt::ImageDex2Oat); - dump_native_stack_on_sig_quit_ = runtime_options.GetOrDefault(Opt::DumpNativeStackOnSigQuit); vfprintf_ = runtime_options.GetOrDefault(Opt::HookVfprintf); exit_ = runtime_options.GetOrDefault(Opt::HookExit); diff --git a/runtime/runtime.h b/runtime/runtime.h index c7f650ea3f..b961e7f39a 100644 --- a/runtime/runtime.h +++ b/runtime/runtime.h @@ -651,10 +651,6 @@ class Runtime { safe_mode_ = mode; } - bool GetDumpNativeStackOnSigQuit() const { - return dump_native_stack_on_sig_quit_; - } - bool GetPrunedDalvikCache() const { return pruned_dalvik_cache_; } @@ -1005,9 +1001,6 @@ class Runtime { // when there is a warning. This is only used for testing. bool always_set_hidden_api_warning_flag_; - // Whether threads should dump their native stack on SIGQUIT. - bool dump_native_stack_on_sig_quit_; - // Whether the dalvik cache was pruned when initializing the runtime. bool pruned_dalvik_cache_; diff --git a/runtime/runtime_common.cc b/runtime/runtime_common.cc index 59af9187f9..41bfb58d93 100644 --- a/runtime/runtime_common.cc +++ b/runtime/runtime_common.cc @@ -41,7 +41,6 @@ namespace art { using android::base::StringPrintf; static constexpr bool kUseSigRTTimeout = true; -static constexpr bool kDumpNativeStackOnTimeout = true; const char* GetSignalName(int signal_number) { switch (signal_number) { @@ -441,7 +440,7 @@ void HandleUnexpectedSignalCommon(int signal_number, // Special timeout signal. Try to dump all threads. // Note: Do not use DumpForSigQuit, as that might disable native unwind, but the native parts // are of value here. - runtime->GetThreadList()->Dump(std::cerr, kDumpNativeStackOnTimeout); + runtime->GetThreadList()->Dump(std::cerr); std::cerr << std::endl; } diff --git a/runtime/runtime_options.def b/runtime/runtime_options.def index 4121ad69ed..dcb1335023 100644 --- a/runtime/runtime_options.def +++ b/runtime/runtime_options.def @@ -70,7 +70,6 @@ RUNTIME_OPTIONS_KEY (Unit, LowMemoryMode) RUNTIME_OPTIONS_KEY (bool, UseTLAB, (kUseTlab || kUseReadBarrier)) RUNTIME_OPTIONS_KEY (bool, EnableHSpaceCompactForOOM, true) RUNTIME_OPTIONS_KEY (bool, UseJitCompilation, false) -RUNTIME_OPTIONS_KEY (bool, DumpNativeStackOnSigQuit, true) RUNTIME_OPTIONS_KEY (bool, MadviseRandomAccess, false) RUNTIME_OPTIONS_KEY (unsigned int, JITCompileThreshold) RUNTIME_OPTIONS_KEY (unsigned int, JITWarmupThreshold) diff --git a/runtime/thread.cc b/runtime/thread.cc index 5b03c2d884..f0bd9aa65e 100644 --- a/runtime/thread.cc +++ b/runtime/thread.cc @@ -1161,10 +1161,9 @@ void Thread::ShortDump(std::ostream& os) const { << "]"; } -void Thread::Dump(std::ostream& os, bool dump_native_stack, BacktraceMap* backtrace_map, - bool force_dump_stack) const { +void Thread::Dump(std::ostream& os, BacktraceMap* backtrace_map, bool force_dump_stack) const { DumpState(os); - DumpStack(os, dump_native_stack, backtrace_map, force_dump_stack); + DumpStack(os, backtrace_map, force_dump_stack); } mirror::String* Thread::GetThreadName() const { @@ -1964,10 +1963,7 @@ void Thread::DumpJavaStack(std::ostream& os, bool check_suspended, bool dump_loc } } -void Thread::DumpStack(std::ostream& os, - bool dump_native_stack, - BacktraceMap* backtrace_map, - bool force_dump_stack) const { +void Thread::DumpStack(std::ostream& os, BacktraceMap* backtrace_map, bool force_dump_stack) const { // TODO: we call this code when dying but may not have suspended the thread ourself. The // IsSuspended check is therefore racy with the use for dumping (normally we inhibit // the race with the thread_suspend_count_lock_). @@ -1980,7 +1976,7 @@ void Thread::DumpStack(std::ostream& os, } if (safe_to_dump || force_dump_stack) { // If we're currently in native code, dump that stack before dumping the managed stack. - if (dump_native_stack && (dump_for_abort || force_dump_stack || ShouldShowNativeStack(this))) { + if (dump_for_abort || force_dump_stack || ShouldShowNativeStack(this)) { DumpKernelStack(os, GetTid(), " kernel: ", false); ArtMethod* method = GetCurrentMethod(nullptr, diff --git a/runtime/thread.h b/runtime/thread.h index 6549fc1a1f..108fbc7f86 100644 --- a/runtime/thread.h +++ b/runtime/thread.h @@ -207,7 +207,6 @@ class Thread { // Dumps the detailed thread state and the thread stack (used for SIGQUIT). void Dump(std::ostream& os, - bool dump_native_stack = true, BacktraceMap* backtrace_map = nullptr, bool force_dump_stack = false) const REQUIRES(!Locks::thread_suspend_count_lock_) @@ -1303,7 +1302,6 @@ class Thread { void DumpState(std::ostream& os) const REQUIRES_SHARED(Locks::mutator_lock_); void DumpStack(std::ostream& os, - bool dump_native_stack = true, BacktraceMap* backtrace_map = nullptr, bool force_dump_stack = false) const REQUIRES(!Locks::thread_suspend_count_lock_) diff --git a/runtime/thread_list.cc b/runtime/thread_list.cc index 8095ef57c7..2e41b9f455 100644 --- a/runtime/thread_list.cc +++ b/runtime/thread_list.cc @@ -152,9 +152,8 @@ void ThreadList::DumpForSigQuit(std::ostream& os) { suspend_all_historam_.PrintConfidenceIntervals(os, 0.99, data); // Dump time to suspend. } } - bool dump_native_stack = Runtime::Current()->GetDumpNativeStackOnSigQuit(); - Dump(os, dump_native_stack); - DumpUnattachedThreads(os, dump_native_stack && kDumpUnattachedThreadNativeStackForSigQuit); + Dump(os); + DumpUnattachedThreads(os, kDumpUnattachedThreadNativeStackForSigQuit); } static void DumpUnattachedThread(std::ostream& os, pid_t tid, bool dump_native_stack) @@ -201,11 +200,10 @@ static constexpr uint32_t kDumpWaitTimeout = kIsTargetBuild ? 100000 : 20000; // A closure used by Thread::Dump. class DumpCheckpoint FINAL : public Closure { public: - DumpCheckpoint(std::ostream* os, bool dump_native_stack) + explicit DumpCheckpoint(std::ostream* os) : os_(os), barrier_(0), - backtrace_map_(dump_native_stack ? BacktraceMap::Create(getpid()) : nullptr), - dump_native_stack_(dump_native_stack) { + backtrace_map_(BacktraceMap::Create(getpid())) { if (backtrace_map_ != nullptr) { backtrace_map_->SetSuffixesToIgnore(std::vector<std::string> { "oat", "odex" }); } @@ -219,7 +217,7 @@ class DumpCheckpoint FINAL : public Closure { std::ostringstream local_os; { ScopedObjectAccess soa(self); - thread->Dump(local_os, dump_native_stack_, backtrace_map_.get()); + thread->Dump(local_os, backtrace_map_.get()); } { // Use the logging lock to ensure serialization when writing to the common ostream. @@ -247,18 +245,16 @@ class DumpCheckpoint FINAL : public Closure { Barrier barrier_; // A backtrace map, so that all threads use a shared info and don't reacquire/parse separately. std::unique_ptr<BacktraceMap> backtrace_map_; - // Whether we should dump the native stack. - const bool dump_native_stack_; }; -void ThreadList::Dump(std::ostream& os, bool dump_native_stack) { +void ThreadList::Dump(std::ostream& os) { Thread* self = Thread::Current(); { MutexLock mu(self, *Locks::thread_list_lock_); os << "DALVIK THREADS (" << list_.size() << "):\n"; } if (self != nullptr) { - DumpCheckpoint checkpoint(&os, dump_native_stack); + DumpCheckpoint checkpoint(&os); size_t threads_running_checkpoint; { // Use SOA to prevent deadlocks if multiple threads are calling Dump() at the same time. @@ -269,7 +265,7 @@ void ThreadList::Dump(std::ostream& os, bool dump_native_stack) { checkpoint.WaitForThreadsToRunThroughCheckpoint(threads_running_checkpoint); } } else { - DumpUnattachedThreads(os, dump_native_stack); + DumpUnattachedThreads(os, /* dump_native_stack */ true); } } @@ -491,7 +487,6 @@ void ThreadList::RunEmptyCheckpoint() { // Found a runnable thread that hasn't responded to the empty checkpoint request. // Assume it's stuck and safe to dump its stack. thread->Dump(LOG_STREAM(FATAL_WITHOUT_ABORT), - /*dump_native_stack*/ true, /*backtrace_map*/ nullptr, /*force_dump_stack*/ true); } diff --git a/runtime/thread_list.h b/runtime/thread_list.h index 895c1a41ce..09b10d2ad3 100644 --- a/runtime/thread_list.h +++ b/runtime/thread_list.h @@ -57,7 +57,7 @@ class ThreadList { void DumpForSigQuit(std::ostream& os) REQUIRES(!Locks::thread_list_lock_, !Locks::mutator_lock_); // For thread suspend timeout dumps. - void Dump(std::ostream& os, bool dump_native_stack = true) + void Dump(std::ostream& os) REQUIRES(!Locks::thread_list_lock_, !Locks::thread_suspend_count_lock_); pid_t GetLockOwner(); // For SignalCatcher. diff --git a/test/1949-short-dex-file/expected.txt b/test/1949-short-dex-file/expected.txt new file mode 100644 index 0000000000..863339fb8c --- /dev/null +++ b/test/1949-short-dex-file/expected.txt @@ -0,0 +1 @@ +Passed diff --git a/test/1949-short-dex-file/info.txt b/test/1949-short-dex-file/info.txt new file mode 100644 index 0000000000..e924086e23 --- /dev/null +++ b/test/1949-short-dex-file/info.txt @@ -0,0 +1,30 @@ +Tests the fix for b/74116990 + +The JIT was reading into incorrect dex files during class redefinition if a +native method was present. + +The transformed dex file is specifically crafted to have exactly 4 methodIDs in +it. They are (in order): + (0) Ljava/lang/Object;-><init>()V + (1) Lxyz/Transform;-><init>()V + (2) Lxyz/Transform;->bar()V + (3) Lxyz/Transform;->foo()V + +In the transformed version of the dex file there is a new method. The new list of methodIDs is: + (0) Lart/Test1949;->doNothing()V + (1) Ljava/lang/Object;-><init>()V + (2) Lxyz/Transform;-><init>()V + (3) Lxyz/Transform;->bar()V + (4) Lxyz/Transform;->foo()V + +This test tries to get the JIT to read out-of-bounds on the initial dex file by getting it to +read the 5th method id of the new file (Lxyz/Transform;->foo()V) from the old dex file (which +only has 4 method ids). + +To do this we need to make sure that the class being transformed is near the end of the +alphabet (package xyz, method foo). If it is further forward than the other method-ids then the +JIT will read an incorrect (but valid) method-id from the old-dex file. This is why the error +wasn't caught in our other tests (package art is always at the front). + +The final method that causes the OOB read needs to be a native method because that is the only +method-type the jit uses dex-file information to keep track of. diff --git a/test/988-method-trace/check b/test/1949-short-dex-file/run index de64a3e17b..c6e62ae6cd 100644..100755 --- a/test/988-method-trace/check +++ b/test/1949-short-dex-file/run @@ -1,12 +1,12 @@ #!/bin/bash # -# Copyright (C) 2017 The Android Open Source Project +# 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 +# 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, @@ -14,10 +14,4 @@ # See the License for the specific language governing permissions and # limitations under the License. -# Building for libcore, this uses @hide API which gives it wrong method trace in the expected.txt -# TODO: would be nice if we could build against core_current jars in the future to avoid this. -if [[ "$NEED_DEX" == true ]]; then - patch -p0 expected.txt < expected_jack.diff >/dev/null -fi - -./default-check "$@" +./default-run "$@" --jvmti diff --git a/test/1949-short-dex-file/src/Main.java b/test/1949-short-dex-file/src/Main.java new file mode 100644 index 0000000000..dbbaa861c9 --- /dev/null +++ b/test/1949-short-dex-file/src/Main.java @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2017 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. + */ + +public class Main { + public static void main(String[] args) throws Exception { + art.Test1949.run(); + } +} diff --git a/test/1949-short-dex-file/src/art/Redefinition.java b/test/1949-short-dex-file/src/art/Redefinition.java new file mode 100644 index 0000000000..56d2938a01 --- /dev/null +++ b/test/1949-short-dex-file/src/art/Redefinition.java @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2017 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/1949-short-dex-file/src/art/Test1949.java b/test/1949-short-dex-file/src/art/Test1949.java new file mode 100644 index 0000000000..98fa7fc2c1 --- /dev/null +++ b/test/1949-short-dex-file/src/art/Test1949.java @@ -0,0 +1,142 @@ +/* + * Copyright (C) 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. + */ + +package art; + +import java.lang.reflect.*; +import java.util.Base64; +import java.nio.ByteBuffer; + +public class Test1949 { + private final static boolean isDalvik = System.getProperty("java.vm.name").equals("Dalvik"); + + // This dex file is specifically crafted to have exactly 4 methodIDs in it. They are (in order): + // (0) Ljava/lang/Object;-><init>()V + // (1) Lxyz/Transform;-><init>()V + // (2) Lxyz/Transform;->bar()V + // (3) Lxyz/Transform;->foo()V + // + // In the transformed version of the dex file there is a new method. The new list of methodIDs is: + // (0) Lart/Test1949;->doNothing()V + // (1) Ljava/lang/Object;-><init>()V + // (2) Lxyz/Transform;-><init>()V + // (3) Lxyz/Transform;->bar()V + // (4) Lxyz/Transform;->foo()V + // + // This test tries to get the JIT to read out-of-bounds on the initial dex file by getting it to + // read the 5th method id of the new file (Lxyz/Transform;->foo()V) from the old dex file (which + // only has 4 method ids). + // + // To do this we need to make sure that the class being transformed is near the end of the + // alphabet (package xyz, method foo). If it is further forward than the other method-ids then the + // JIT will read an incorrect (but valid) method-id from the old-dex file. This is why the error + // wasn't caught in our other tests (package art is always at the front). + // + // The final method that causes the OOB read needs to be a native method because that is the only + // method-type the jit uses dex-file information to keep track of. + + /** + * base64 encoded class/dex file for + * package xyz; + * public class Transform { + * public native void foo(); + * public void bar() {} + * } + */ + private static final byte[] CLASS_BYTES_INIT = Base64.getDecoder().decode( + "yv66vgAAADUADwoAAwAMBwANBwAOAQAGPGluaXQ+AQADKClWAQAEQ29kZQEAD0xpbmVOdW1iZXJU" + + "YWJsZQEAA2ZvbwEAA2JhcgEAClNvdXJjZUZpbGUBAA5UcmFuc2Zvcm0uamF2YQwABAAFAQANeHl6" + + "L1RyYW5zZm9ybQEAEGphdmEvbGFuZy9PYmplY3QAIQACAAMAAAAAAAMAAQAEAAUAAQAGAAAAHQAB" + + "AAEAAAAFKrcAAbEAAAABAAcAAAAGAAEAAAACAQEACAAFAAAAAQAJAAUAAQAGAAAAGQAAAAEAAAAB" + + "sQAAAAEABwAAAAYAAQAAAAQAAQAKAAAAAgAL"); + private static final byte[] DEX_BYTES_INIT = Base64.getDecoder().decode( + "ZGV4CjAzNQBDUutFJpeT+okk+aXah8NQ61q2XRtkmChwAgAAcAAAAHhWNBIAAAAAAAAAANwBAAAI" + + "AAAAcAAAAAMAAACQAAAAAQAAAJwAAAAAAAAAAAAAAAQAAACoAAAAAQAAAMgAAACIAQAA6AAAABwB" + + "AAAkAQAAOAEAAEkBAABZAQAAXAEAAGEBAABmAQAAAQAAAAIAAAAEAAAABAAAAAIAAAAAAAAAAAAA" + + "AAAAAAABAAAAAAAAAAEAAAAFAAAAAQAAAAYAAAABAAAAAQAAAAAAAAAAAAAAAwAAAAAAAADDAQAA" + + "AAAAAAEAAQABAAAAEgEAAAQAAABwEAAAAAAOAAEAAQAAAAAAFgEAAAEAAAAOAAIADgAEAA4AAAAG" + + "PGluaXQ+ABJMamF2YS9sYW5nL09iamVjdDsAD0x4eXovVHJhbnNmb3JtOwAOVHJhbnNmb3JtLmph" + + "dmEAAVYAA2JhcgADZm9vAFt+fkQ4eyJtaW4tYXBpIjoxLCJzaGEtMSI6IjkwZWYyMjkwNWMzZmVj" + + "Y2FiMjMwMzBhNmJkYzU2NTcwYTMzNWVmMDUiLCJ2ZXJzaW9uIjoidjEuMS44LWRldiJ9AAAAAQIB" + + "gYAE6AECAYACAYECAAAAAAAAAAAMAAAAAAAAAAEAAAAAAAAAAQAAAAgAAABwAAAAAgAAAAMAAACQ" + + "AAAAAwAAAAEAAACcAAAABQAAAAQAAACoAAAABgAAAAEAAADIAAAAASAAAAIAAADoAAAAAyAAAAIA" + + "AAASAQAAAiAAAAgAAAAcAQAAACAAAAEAAADDAQAAAxAAAAEAAADYAQAAABAAAAEAAADcAQAA"); + + /** + * base64 encoded class/dex file for + * package xyz; + * public class Transform { + * public native void foo(); + * public void bar() { + * // Make sure the methodID is before any of the ones in Transform + * art.Test1949.doNothing(); + * } + * } + */ + private static final byte[] CLASS_BYTES_FINAL = Base64.getDecoder().decode( + "yv66vgAAADUAFAoABAANCgAOAA8HABAHABEBAAY8aW5pdD4BAAMoKVYBAARDb2RlAQAPTGluZU51" + + "bWJlclRhYmxlAQADZm9vAQADYmFyAQAKU291cmNlRmlsZQEADlRyYW5zZm9ybS5qYXZhDAAFAAYH" + + "ABIMABMABgEADXh5ei9UcmFuc2Zvcm0BABBqYXZhL2xhbmcvT2JqZWN0AQAMYXJ0L1Rlc3QxOTQ5" + + "AQAJZG9Ob3RoaW5nACEAAwAEAAAAAAADAAEABQAGAAEABwAAAB0AAQABAAAABSq3AAGxAAAAAQAI" + + "AAAABgABAAAAAgEBAAkABgAAAAEACgAGAAEABwAAABwAAAABAAAABLgAArEAAAABAAgAAAAGAAEA" + + "AAAEAAEACwAAAAIADA=="); + private static final byte[] DEX_BYTES_FINAL = Base64.getDecoder().decode( + "ZGV4CjAzNQBHXBiw7Hso1vnmaXE1VCV41f4+0aECixOgAgAAcAAAAHhWNBIAAAAAAAAAAAwCAAAK" + + "AAAAcAAAAAQAAACYAAAAAQAAAKgAAAAAAAAAAAAAAAUAAAC0AAAAAQAAANwAAACkAQAA/AAAADQB" + + "AAA8AQAATAEAAGABAABxAQAAgQEAAIQBAACJAQAAlAEAAJkBAAABAAAAAgAAAAMAAAAFAAAABQAA" + + "AAMAAAAAAAAAAAAAAAcAAAABAAAAAAAAAAIAAAAAAAAAAgAAAAYAAAACAAAACAAAAAIAAAABAAAA" + + "AQAAAAAAAAAEAAAAAAAAAPYBAAAAAAAAAQABAAEAAAAsAQAABAAAAHAQAQAAAA4AAQABAAAAAAAw" + + "AQAABAAAAHEAAAAAAA4AAgAOAAQADgAGPGluaXQ+AA5MYXJ0L1Rlc3QxOTQ5OwASTGphdmEvbGFu" + + "Zy9PYmplY3Q7AA9MeHl6L1RyYW5zZm9ybTsADlRyYW5zZm9ybS5qYXZhAAFWAANiYXIACWRvTm90" + + "aGluZwADZm9vAFt+fkQ4eyJtaW4tYXBpIjoxLCJzaGEtMSI6IjkwZWYyMjkwNWMzZmVjY2FiMjMw" + + "MzBhNmJkYzU2NTcwYTMzNWVmMDUiLCJ2ZXJzaW9uIjoidjEuMS44LWRldiJ9AAAAAQICgYAE/AED" + + "AZQCAYECAAAAAAAMAAAAAAAAAAEAAAAAAAAAAQAAAAoAAABwAAAAAgAAAAQAAACYAAAAAwAAAAEA" + + "AACoAAAABQAAAAUAAAC0AAAABgAAAAEAAADcAAAAASAAAAIAAAD8AAAAAyAAAAIAAAAsAQAAAiAA" + + "AAoAAAA0AQAAACAAAAEAAAD2AQAAAxAAAAEAAAAIAgAAABAAAAEAAAAMAgAA"); + + public static void run() throws Exception { + Redefinition.setTestConfiguration(Redefinition.Config.COMMON_REDEFINE); + doTest(); + } + + // A method with a methodID before anything in Transform. + public static void doNothing() {} + + private static ClassLoader CreateClassLoader(byte[] clz, byte[] dex) throws Exception { + if (isDalvik) { + Class<?> class_loader_class = Class.forName("dalvik.system.InMemoryDexClassLoader"); + Constructor<?> ctor = class_loader_class.getConstructor(ByteBuffer.class, ClassLoader.class); + /* on Dalvik, this is a DexFile; otherwise, it's null */ + return (ClassLoader)ctor.newInstance(ByteBuffer.wrap(dex), Test1949.class.getClassLoader()); + } else { + return new ClassLoader() { + public Class<?> findClass(String name) throws ClassNotFoundException { + if (name.equals("xyz.Transform")) { + return defineClass(name, clz, 0, clz.length); + } else { + throw new ClassNotFoundException("Couldn't find class: " + name); + } + } + }; + } + } + + public static void doTest() throws Exception { + Class c = CreateClassLoader(CLASS_BYTES_INIT, DEX_BYTES_INIT).loadClass("xyz.Transform"); + Redefinition.doCommonClassRedefinition(c, CLASS_BYTES_FINAL, DEX_BYTES_FINAL); + System.out.println("Passed"); + } +} diff --git a/test/988-method-trace/expected.txt b/test/988-method-trace/expected.txt index 574d5b0772..7f64e23a77 100644 --- a/test/988-method-trace/expected.txt +++ b/test/988-method-trace/expected.txt @@ -107,8 +107,8 @@ fibonacci(5)=5 ......=> public static char[] java.util.Arrays.copyOf(char[],int) .......=> public static int java.lang.Math.min(int,int) .......<= public static int java.lang.Math.min(int,int) -> <class java.lang.Integer: 16> -.......=> public static void java.lang.System.arraycopy(char[],int,char[],int,int) -.......<= public static void java.lang.System.arraycopy(char[],int,char[],int,int) -> <null: null> +.......=> public static void java.lang.System.arraycopy(java.lang.Object,int,java.lang.Object,int,int) +.......<= public static void java.lang.System.arraycopy(java.lang.Object,int,java.lang.Object,int,int) -> <null: null> ......<= public static char[] java.util.Arrays.copyOf(char[],int) -> <class [C: [B, a, d, , a, r, g, u, m, e, n, t, :, , -, 1, 9, , <, , 0, <control-0000>, <control-0000>, <control-0000>, <control-0000>, <control-0000>, <control-0000>, <control-0000>, <control-0000>, <control-0000>, <control-0000>, <control-0000>, <control-0000>, <control-0000>]> .....<= private void java.lang.AbstractStringBuilder.ensureCapacityInternal(int) -> <null: null> .....=> static void java.lang.Integer.getChars(int,int,char[]) @@ -208,8 +208,8 @@ fibonacci(-19) -> java.lang.Error: Bad argument: -19 < 0 ......=> public static char[] java.util.Arrays.copyOf(char[],int) .......=> public static int java.lang.Math.min(int,int) .......<= public static int java.lang.Math.min(int,int) -> <class java.lang.Integer: 16> -.......=> public static void java.lang.System.arraycopy(char[],int,char[],int,int) -.......<= public static void java.lang.System.arraycopy(char[],int,char[],int,int) -> <null: null> +.......=> public static void java.lang.System.arraycopy(java.lang.Object,int,java.lang.Object,int,int) +.......<= public static void java.lang.System.arraycopy(java.lang.Object,int,java.lang.Object,int,int) -> <null: null> ......<= public static char[] java.util.Arrays.copyOf(char[],int) -> <class [C: [B, a, d, , a, r, g, u, m, e, n, t, :, , -, 1, 9, , <, , 0, <control-0000>, <control-0000>, <control-0000>, <control-0000>, <control-0000>, <control-0000>, <control-0000>, <control-0000>, <control-0000>, <control-0000>, <control-0000>, <control-0000>, <control-0000>]> .....<= private void java.lang.AbstractStringBuilder.ensureCapacityInternal(int) -> <null: null> .....=> static void java.lang.Integer.getChars(int,int,char[]) diff --git a/test/988-method-trace/expected_jack.diff b/test/988-method-trace/expected_jack.diff deleted file mode 100644 index 11364a0539..0000000000 --- a/test/988-method-trace/expected_jack.diff +++ /dev/null @@ -1,10 +0,0 @@ -450,453c450,453 -< .=> public static void java.lang.System.arraycopy(java.lang.Object,int,java.lang.Object,int,int) -< .<= public static void java.lang.System.arraycopy(java.lang.Object,int,java.lang.Object,int,int) -> <null: null> -< .=> public static void java.lang.System.arraycopy(java.lang.Object,int,java.lang.Object,int,int) -< .<= public static void java.lang.System.arraycopy(java.lang.Object,int,java.lang.Object,int,int) -> <null: null> ---- -> .=> public static void java.lang.System.arraycopy(int[],int,int[],int,int) -> .<= public static void java.lang.System.arraycopy(int[],int,int[],int,int) -> <null: null> -> .=> public static void java.lang.System.arraycopy(char[],int,char[],int,int) -> .<= public static void java.lang.System.arraycopy(char[],int,char[],int,int) -> <null: null> diff --git a/test/etc/run-test-jar b/test/etc/run-test-jar index 6444eb9a89..86adb733a9 100755 --- a/test/etc/run-test-jar +++ b/test/etc/run-test-jar @@ -775,9 +775,6 @@ if [ "$HOST" = "n" ]; then TMP_DIR_OPTION="-Djava.io.tmpdir=/data/local/tmp" fi -# We set DumpNativeStackOnSigQuit to false to avoid stressing libunwind. -# b/27185632 -# b/24664297 dalvikvm_cmdline="$INVOKE_WITH $GDB $ANDROID_ROOT/bin/$DALVIKVM \ $GDB_ARGS \ $FLAGS \ @@ -868,7 +865,12 @@ if [ "$HOST" = "n" ]; then fi # System libraries needed by libarttestd.so - PUBLIC_LIBS=libart.so:libartd.so:libc++.so:libbacktrace.so:libdexfile.so:libdexfiled.so:libbase.so:libnativehelper.so + PUBLIC_LIBS=libc++.so:libbacktrace.so:libbase.so:libnativehelper.so + if [ "$TEST_IS_NDEBUG" = "y" ]; then + PUBLIC_LIBS=$PUBLIC_LIBS:libart.so:libdexfile.so + else + PUBLIC_LIBS=$PUBLIC_LIBS:libartd.so:libdexfiled.so + fi # Create a script with the command. The command can get longer than the longest # allowed adb command and there is no way to get the exit status from a adb shell diff --git a/test/testrunner/target_config.py b/test/testrunner/target_config.py index 9d0377510a..2c433af512 100644 --- a/test/testrunner/target_config.py +++ b/test/testrunner/target_config.py @@ -69,7 +69,13 @@ target_config = { } }, 'art-gcstress-gcverify': { - 'run-test': ['--gcstress', + # Don't include --interpreter, because it takes too long to run all + # the tests on the build bot (b/74225325) + 'run-test': ['--interp-ac', + '--jit', + '--optimizing', + '--speed-profile', + '--gcstress', '--gcverify'], 'env' : { 'ART_USE_READ_BARRIER' : 'false', |