summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--libartbase/base/unix_file/fd_file.cc2
-rw-r--r--openjdkjvmti/ti_redefine.cc24
-rw-r--r--runtime/gc/heap.cc37
-rw-r--r--runtime/gc/heap.h3
-rw-r--r--runtime/jit/jit_code_cache.cc23
-rw-r--r--runtime/native/dalvik_system_ZygoteHooks.cc2
-rw-r--r--runtime/oat_file_assistant.cc6
-rw-r--r--runtime/parsed_options.cc5
-rw-r--r--runtime/runtime.cc2
-rw-r--r--runtime/runtime.h7
-rw-r--r--runtime/runtime_common.cc3
-rw-r--r--runtime/runtime_options.def1
-rw-r--r--runtime/thread.cc12
-rw-r--r--runtime/thread.h2
-rw-r--r--runtime/thread_list.cc21
-rw-r--r--runtime/thread_list.h2
-rw-r--r--test/1949-short-dex-file/expected.txt1
-rw-r--r--test/1949-short-dex-file/info.txt30
-rwxr-xr-x[-rw-r--r--]test/1949-short-dex-file/run (renamed from test/988-method-trace/check)12
-rw-r--r--test/1949-short-dex-file/src/Main.java21
-rw-r--r--test/1949-short-dex-file/src/art/Redefinition.java91
-rw-r--r--test/1949-short-dex-file/src/art/Test1949.java142
-rw-r--r--test/988-method-trace/expected.txt8
-rw-r--r--test/988-method-trace/expected_jack.diff10
-rwxr-xr-xtest/etc/run-test-jar10
-rw-r--r--test/testrunner/target_config.py8
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',