diff options
Diffstat (limited to 'runtime')
| -rw-r--r-- | runtime/class_linker.cc | 8 | ||||
| -rw-r--r-- | runtime/debugger.cc | 190 | ||||
| -rw-r--r-- | runtime/debugger.h | 40 | ||||
| -rw-r--r-- | runtime/gc/space/image_space.cc | 11 | ||||
| -rw-r--r-- | runtime/gc/space/image_space.h | 3 | ||||
| -rw-r--r-- | runtime/implicit_check_options.h | 118 | ||||
| -rw-r--r-- | runtime/jdwp/jdwp_event.cc | 20 | ||||
| -rw-r--r-- | runtime/monitor.cc | 7 | ||||
| -rw-r--r-- | runtime/monitor_test.cc | 380 | ||||
| -rw-r--r-- | runtime/oat.cc | 145 | ||||
| -rw-r--r-- | runtime/oat.h | 42 | ||||
| -rw-r--r-- | runtime/oat_file.cc | 80 | ||||
| -rw-r--r-- | runtime/runtime.cc | 1 | ||||
| -rw-r--r-- | runtime/verifier/method_verifier.cc | 48 | ||||
| -rw-r--r-- | runtime/verifier/method_verifier.h | 4 | ||||
| -rw-r--r-- | runtime/verifier/reg_type.h | 4 |
16 files changed, 914 insertions, 187 deletions
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index 60453c3cc1..860cbd207e 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -581,10 +581,6 @@ bool ClassLinker::GenerateOatFile(const char* dex_filename, std::vector<std::string> argv; argv.push_back(dex2oat); argv.push_back("--runtime-arg"); - argv.push_back("-Xms64m"); - argv.push_back("--runtime-arg"); - argv.push_back("-Xmx64m"); - argv.push_back("--runtime-arg"); argv.push_back("-classpath"); argv.push_back("--runtime-arg"); argv.push_back(Runtime::Current()->GetClassPathString()); @@ -1163,7 +1159,9 @@ void ClassLinker::InitFromImage() { OatFile& oat_file = GetImageOatFile(space); CHECK_EQ(oat_file.GetOatHeader().GetImageFileLocationOatChecksum(), 0U); CHECK_EQ(oat_file.GetOatHeader().GetImageFileLocationOatDataBegin(), 0U); - CHECK(oat_file.GetOatHeader().GetImageFileLocation().empty()); + const char* image_file_location = oat_file.GetOatHeader(). + GetStoreValueByKey(OatHeader::kImageLocationKey); + CHECK(image_file_location == nullptr || *image_file_location == 0); portable_resolution_trampoline_ = oat_file.GetOatHeader().GetPortableResolutionTrampoline(); quick_resolution_trampoline_ = oat_file.GetOatHeader().GetQuickResolutionTrampoline(); portable_imt_conflict_trampoline_ = oat_file.GetOatHeader().GetPortableImtConflictTrampoline(); diff --git a/runtime/debugger.cc b/runtime/debugger.cc index 11415b49e0..6161aff647 100644 --- a/runtime/debugger.cc +++ b/runtime/debugger.cc @@ -170,27 +170,47 @@ class AllocRecord { AllocRecordStackTraceElement stack_[kMaxAllocRecordStackDepth]; // Unused entries have NULL method. }; -struct Breakpoint { - // The location of this breakpoint. - mirror::ArtMethod* method; - uint32_t dex_pc; +class Breakpoint { + public: + Breakpoint(mirror::ArtMethod* method, uint32_t dex_pc, bool need_full_deoptimization) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) + : method_(nullptr), dex_pc_(dex_pc), need_full_deoptimization_(need_full_deoptimization) { + ScopedObjectAccessUnchecked soa(Thread::Current()); + method_ = soa.EncodeMethod(method); + } - // Indicates whether breakpoint needs full deoptimization or selective deoptimization. - bool need_full_deoptimization; + Breakpoint(const Breakpoint& other) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) + : method_(nullptr), dex_pc_(other.dex_pc_), + need_full_deoptimization_(other.need_full_deoptimization_) { + ScopedObjectAccessUnchecked soa(Thread::Current()); + method_ = soa.EncodeMethod(other.Method()); + } - Breakpoint(mirror::ArtMethod* method, uint32_t dex_pc, bool need_full_deoptimization) - : method(method), dex_pc(dex_pc), need_full_deoptimization(need_full_deoptimization) {} + mirror::ArtMethod* Method() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + ScopedObjectAccessUnchecked soa(Thread::Current()); + return soa.DecodeMethod(method_); + } - void VisitRoots(RootCallback* callback, void* arg) { - if (method != nullptr) { - callback(reinterpret_cast<mirror::Object**>(&method), arg, 0, kRootDebugger); - } + uint32_t DexPc() const { + return dex_pc_; + } + + bool NeedFullDeoptimization() const { + return need_full_deoptimization_; } + + private: + // The location of this breakpoint. + jmethodID method_; + uint32_t dex_pc_; + + // Indicates whether breakpoint needs full deoptimization or selective deoptimization. + bool need_full_deoptimization_; }; -static std::ostream& operator<<(std::ostream& os, const Breakpoint& rhs) +static std::ostream& operator<<(std::ostream& os, Breakpoint& rhs) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - os << StringPrintf("Breakpoint[%s @%#x]", PrettyMethod(rhs.method).c_str(), rhs.dex_pc); + os << StringPrintf("Breakpoint[%s @%#x]", PrettyMethod(rhs.Method()).c_str(), rhs.DexPc()); return os; } @@ -349,18 +369,12 @@ void SingleStepControl::Clear() { dex_pcs.clear(); } -void DeoptimizationRequest::VisitRoots(RootCallback* callback, void* arg) { - if (method != nullptr) { - callback(reinterpret_cast<mirror::Object**>(&method), arg, 0, kRootDebugger); - } -} - static bool IsBreakpoint(const mirror::ArtMethod* m, uint32_t dex_pc) LOCKS_EXCLUDED(Locks::breakpoint_lock_) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { MutexLock mu(Thread::Current(), *Locks::breakpoint_lock_); for (size_t i = 0, e = gBreakpoints.size(); i < e; ++i) { - if (gBreakpoints[i].method == m && gBreakpoints[i].dex_pc == dex_pc) { + if (gBreakpoints[i].DexPc() == dex_pc && gBreakpoints[i].Method() == m) { VLOG(jdwp) << "Hit breakpoint #" << i << ": " << gBreakpoints[i]; return true; } @@ -641,21 +655,6 @@ void Dbg::StartJdwp() { } } -void Dbg::VisitRoots(RootCallback* callback, void* arg) { - { - MutexLock mu(Thread::Current(), *Locks::breakpoint_lock_); - for (Breakpoint& bp : gBreakpoints) { - bp.VisitRoots(callback, arg); - } - } - if (deoptimization_lock_ != nullptr) { // only true if the debugger is started. - MutexLock mu(Thread::Current(), *deoptimization_lock_); - for (DeoptimizationRequest& req : deoptimization_requests_) { - req.VisitRoots(callback, arg); - } - } -} - void Dbg::StopJdwp() { // Prevent the JDWP thread from processing JDWP incoming packets after we close the connection. Disposed(); @@ -2844,22 +2843,22 @@ size_t* Dbg::GetReferenceCounterForEvent(uint32_t instrumentation_event) { // Process request while all mutator threads are suspended. void Dbg::ProcessDeoptimizationRequest(const DeoptimizationRequest& request) { instrumentation::Instrumentation* instrumentation = Runtime::Current()->GetInstrumentation(); - switch (request.kind) { + switch (request.GetKind()) { case DeoptimizationRequest::kNothing: LOG(WARNING) << "Ignoring empty deoptimization request."; break; case DeoptimizationRequest::kRegisterForEvent: VLOG(jdwp) << StringPrintf("Add debugger as listener for instrumentation event 0x%x", - request.instrumentation_event); - instrumentation->AddListener(&gDebugInstrumentationListener, request.instrumentation_event); - instrumentation_events_ |= request.instrumentation_event; + request.InstrumentationEvent()); + instrumentation->AddListener(&gDebugInstrumentationListener, request.InstrumentationEvent()); + instrumentation_events_ |= request.InstrumentationEvent(); break; case DeoptimizationRequest::kUnregisterForEvent: VLOG(jdwp) << StringPrintf("Remove debugger as listener for instrumentation event 0x%x", - request.instrumentation_event); + request.InstrumentationEvent()); instrumentation->RemoveListener(&gDebugInstrumentationListener, - request.instrumentation_event); - instrumentation_events_ &= ~request.instrumentation_event; + request.InstrumentationEvent()); + instrumentation_events_ &= ~request.InstrumentationEvent(); break; case DeoptimizationRequest::kFullDeoptimization: VLOG(jdwp) << "Deoptimize the world ..."; @@ -2872,17 +2871,17 @@ void Dbg::ProcessDeoptimizationRequest(const DeoptimizationRequest& request) { VLOG(jdwp) << "Undeoptimize the world DONE"; break; case DeoptimizationRequest::kSelectiveDeoptimization: - VLOG(jdwp) << "Deoptimize method " << PrettyMethod(request.method) << " ..."; - instrumentation->Deoptimize(request.method); - VLOG(jdwp) << "Deoptimize method " << PrettyMethod(request.method) << " DONE"; + VLOG(jdwp) << "Deoptimize method " << PrettyMethod(request.Method()) << " ..."; + instrumentation->Deoptimize(request.Method()); + VLOG(jdwp) << "Deoptimize method " << PrettyMethod(request.Method()) << " DONE"; break; case DeoptimizationRequest::kSelectiveUndeoptimization: - VLOG(jdwp) << "Undeoptimize method " << PrettyMethod(request.method) << " ..."; - instrumentation->Undeoptimize(request.method); - VLOG(jdwp) << "Undeoptimize method " << PrettyMethod(request.method) << " DONE"; + VLOG(jdwp) << "Undeoptimize method " << PrettyMethod(request.Method()) << " ..."; + instrumentation->Undeoptimize(request.Method()); + VLOG(jdwp) << "Undeoptimize method " << PrettyMethod(request.Method()) << " DONE"; break; default: - LOG(FATAL) << "Unsupported deoptimization request kind " << request.kind; + LOG(FATAL) << "Unsupported deoptimization request kind " << request.GetKind(); break; } } @@ -2899,8 +2898,8 @@ void Dbg::ProcessDelayedFullUndeoptimizations() { MutexLock mu(Thread::Current(), *deoptimization_lock_); while (delayed_full_undeoptimization_count_ > 0) { DeoptimizationRequest req; - req.kind = DeoptimizationRequest::kFullUndeoptimization; - req.method = nullptr; + req.SetKind(DeoptimizationRequest::kFullUndeoptimization); + req.SetMethod(nullptr); RequestDeoptimizationLocked(req); --delayed_full_undeoptimization_count_; } @@ -2909,7 +2908,7 @@ void Dbg::ProcessDelayedFullUndeoptimizations() { } void Dbg::RequestDeoptimization(const DeoptimizationRequest& req) { - if (req.kind == DeoptimizationRequest::kNothing) { + if (req.GetKind() == DeoptimizationRequest::kNothing) { // Nothing to do. return; } @@ -2918,35 +2917,35 @@ void Dbg::RequestDeoptimization(const DeoptimizationRequest& req) { } void Dbg::RequestDeoptimizationLocked(const DeoptimizationRequest& req) { - switch (req.kind) { + switch (req.GetKind()) { case DeoptimizationRequest::kRegisterForEvent: { - DCHECK_NE(req.instrumentation_event, 0u); - size_t* counter = GetReferenceCounterForEvent(req.instrumentation_event); + DCHECK_NE(req.InstrumentationEvent(), 0u); + size_t* counter = GetReferenceCounterForEvent(req.InstrumentationEvent()); CHECK(counter != nullptr) << StringPrintf("No counter for instrumentation event 0x%x", - req.instrumentation_event); + req.InstrumentationEvent()); if (*counter == 0) { VLOG(jdwp) << StringPrintf("Queue request #%zd to start listening to instrumentation event 0x%x", - deoptimization_requests_.size(), req.instrumentation_event); + deoptimization_requests_.size(), req.InstrumentationEvent()); deoptimization_requests_.push_back(req); } *counter = *counter + 1; break; } case DeoptimizationRequest::kUnregisterForEvent: { - DCHECK_NE(req.instrumentation_event, 0u); - size_t* counter = GetReferenceCounterForEvent(req.instrumentation_event); + DCHECK_NE(req.InstrumentationEvent(), 0u); + size_t* counter = GetReferenceCounterForEvent(req.InstrumentationEvent()); CHECK(counter != nullptr) << StringPrintf("No counter for instrumentation event 0x%x", - req.instrumentation_event); + req.InstrumentationEvent()); *counter = *counter - 1; if (*counter == 0) { VLOG(jdwp) << StringPrintf("Queue request #%zd to stop listening to instrumentation event 0x%x", - deoptimization_requests_.size(), req.instrumentation_event); + deoptimization_requests_.size(), req.InstrumentationEvent()); deoptimization_requests_.push_back(req); } break; } case DeoptimizationRequest::kFullDeoptimization: { - DCHECK(req.method == nullptr); + DCHECK(req.Method() == nullptr); if (full_deoptimization_event_count_ == 0) { VLOG(jdwp) << "Queue request #" << deoptimization_requests_.size() << " for full deoptimization"; @@ -2956,7 +2955,7 @@ void Dbg::RequestDeoptimizationLocked(const DeoptimizationRequest& req) { break; } case DeoptimizationRequest::kFullUndeoptimization: { - DCHECK(req.method == nullptr); + DCHECK(req.Method() == nullptr); DCHECK_GT(full_deoptimization_event_count_, 0U); --full_deoptimization_event_count_; if (full_deoptimization_event_count_ == 0) { @@ -2967,21 +2966,21 @@ void Dbg::RequestDeoptimizationLocked(const DeoptimizationRequest& req) { break; } case DeoptimizationRequest::kSelectiveDeoptimization: { - DCHECK(req.method != nullptr); + DCHECK(req.Method() != nullptr); VLOG(jdwp) << "Queue request #" << deoptimization_requests_.size() - << " for deoptimization of " << PrettyMethod(req.method); + << " for deoptimization of " << PrettyMethod(req.Method()); deoptimization_requests_.push_back(req); break; } case DeoptimizationRequest::kSelectiveUndeoptimization: { - DCHECK(req.method != nullptr); + DCHECK(req.Method() != nullptr); VLOG(jdwp) << "Queue request #" << deoptimization_requests_.size() - << " for undeoptimization of " << PrettyMethod(req.method); + << " for undeoptimization of " << PrettyMethod(req.Method()); deoptimization_requests_.push_back(req); break; } default: { - LOG(FATAL) << "Unknown deoptimization request kind " << req.kind; + LOG(FATAL) << "Unknown deoptimization request kind " << req.GetKind(); break; } } @@ -3005,7 +3004,7 @@ void Dbg::ManageDeoptimization() { { MutexLock mu(self, *deoptimization_lock_); size_t req_index = 0; - for (const DeoptimizationRequest& request : deoptimization_requests_) { + for (DeoptimizationRequest& request : deoptimization_requests_) { VLOG(jdwp) << "Process deoptimization request #" << req_index++; ProcessDeoptimizationRequest(request); } @@ -3036,9 +3035,9 @@ static bool IsMethodPossiblyInlined(Thread* self, mirror::ArtMethod* m) } static const Breakpoint* FindFirstBreakpointForMethod(mirror::ArtMethod* m) - EXCLUSIVE_LOCKS_REQUIRED(Locks::breakpoint_lock_) { - for (const Breakpoint& breakpoint : gBreakpoints) { - if (breakpoint.method == m) { + EXCLUSIVE_LOCKS_REQUIRED(Locks::breakpoint_lock_) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + for (Breakpoint& breakpoint : gBreakpoints) { + if (breakpoint.Method() == m) { return &breakpoint; } } @@ -3050,7 +3049,7 @@ static void SanityCheckExistingBreakpoints(mirror::ArtMethod* m, bool need_full_ EXCLUSIVE_LOCKS_REQUIRED(Locks::breakpoint_lock_) { if (kIsDebugBuild) { for (const Breakpoint& breakpoint : gBreakpoints) { - CHECK_EQ(need_full_deoptimization, breakpoint.need_full_deoptimization); + CHECK_EQ(need_full_deoptimization, breakpoint.NeedFullDeoptimization()); } if (need_full_deoptimization) { // We should have deoptimized everything but not "selectively" deoptimized this method. @@ -3080,18 +3079,18 @@ void Dbg::WatchLocation(const JDWP::JdwpLocation* location, DeoptimizationReques // inlined, we deoptimize everything; otherwise we deoptimize only this method. need_full_deoptimization = IsMethodPossiblyInlined(self, m); if (need_full_deoptimization) { - req->kind = DeoptimizationRequest::kFullDeoptimization; - req->method = nullptr; + req->SetKind(DeoptimizationRequest::kFullDeoptimization); + req->SetMethod(nullptr); } else { - req->kind = DeoptimizationRequest::kSelectiveDeoptimization; - req->method = m; + req->SetKind(DeoptimizationRequest::kSelectiveDeoptimization); + req->SetMethod(m); } } else { // There is at least one breakpoint for this method: we don't need to deoptimize. - req->kind = DeoptimizationRequest::kNothing; - req->method = nullptr; + req->SetKind(DeoptimizationRequest::kNothing); + req->SetMethod(nullptr); - need_full_deoptimization = existing_breakpoint->need_full_deoptimization; + need_full_deoptimization = existing_breakpoint->NeedFullDeoptimization(); SanityCheckExistingBreakpoints(m, need_full_deoptimization); } @@ -3103,15 +3102,14 @@ void Dbg::WatchLocation(const JDWP::JdwpLocation* location, DeoptimizationReques // Uninstalls a breakpoint at the specified location. Also indicates through the deoptimization // request if we need to undeoptimize. void Dbg::UnwatchLocation(const JDWP::JdwpLocation* location, DeoptimizationRequest* req) { + MutexLock mu(Thread::Current(), *Locks::breakpoint_lock_); mirror::ArtMethod* m = FromMethodId(location->method_id); DCHECK(m != nullptr) << "No method for method id " << location->method_id; - - MutexLock mu(Thread::Current(), *Locks::breakpoint_lock_); bool need_full_deoptimization = false; for (size_t i = 0, e = gBreakpoints.size(); i < e; ++i) { - if (gBreakpoints[i].method == m && gBreakpoints[i].dex_pc == location->dex_pc) { + if (gBreakpoints[i].DexPc() == location->dex_pc && gBreakpoints[i].Method() == m) { VLOG(jdwp) << "Removed breakpoint #" << i << ": " << gBreakpoints[i]; - need_full_deoptimization = gBreakpoints[i].need_full_deoptimization; + need_full_deoptimization = gBreakpoints[i].NeedFullDeoptimization(); DCHECK_NE(need_full_deoptimization, Runtime::Current()->GetInstrumentation()->IsDeoptimized(m)); gBreakpoints.erase(gBreakpoints.begin() + i); break; @@ -3122,17 +3120,17 @@ void Dbg::UnwatchLocation(const JDWP::JdwpLocation* location, DeoptimizationRequ // There is no more breakpoint on this method: we need to undeoptimize. if (need_full_deoptimization) { // This method required full deoptimization: we need to undeoptimize everything. - req->kind = DeoptimizationRequest::kFullUndeoptimization; - req->method = nullptr; + req->SetKind(DeoptimizationRequest::kFullUndeoptimization); + req->SetMethod(nullptr); } else { // This method required selective deoptimization: we need to undeoptimize only that method. - req->kind = DeoptimizationRequest::kSelectiveUndeoptimization; - req->method = m; + req->SetKind(DeoptimizationRequest::kSelectiveUndeoptimization); + req->SetMethod(m); } } else { // There is at least one breakpoint for this method: we don't need to undeoptimize. - req->kind = DeoptimizationRequest::kNothing; - req->method = nullptr; + req->SetKind(DeoptimizationRequest::kNothing); + req->SetMethod(nullptr); SanityCheckExistingBreakpoints(m, need_full_deoptimization); } } @@ -4590,4 +4588,14 @@ jbyteArray Dbg::GetRecentAllocations() { return result; } +mirror::ArtMethod* DeoptimizationRequest::Method() const { + ScopedObjectAccessUnchecked soa(Thread::Current()); + return soa.DecodeMethod(method_); +} + +void DeoptimizationRequest::SetMethod(mirror::ArtMethod* m) { + ScopedObjectAccessUnchecked soa(Thread::Current()); + method_ = soa.EncodeMethod(m); +} + } // namespace art diff --git a/runtime/debugger.h b/runtime/debugger.h index 2589638461..1d3668c1f6 100644 --- a/runtime/debugger.h +++ b/runtime/debugger.h @@ -131,7 +131,8 @@ struct SingleStepControl { }; // TODO rename to InstrumentationRequest. -struct DeoptimizationRequest { +class DeoptimizationRequest { + public: enum Kind { kNothing, // no action. kRegisterForEvent, // start listening for instrumentation event. @@ -142,21 +143,48 @@ struct DeoptimizationRequest { kSelectiveUndeoptimization // undeoptimize one method. }; - DeoptimizationRequest() : kind(kNothing), instrumentation_event(0), method(nullptr) {} + DeoptimizationRequest() : kind_(kNothing), instrumentation_event_(0), method_(nullptr) {} + + DeoptimizationRequest(const DeoptimizationRequest& other) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) + : kind_(other.kind_), instrumentation_event_(other.instrumentation_event_) { + // Create a new JNI global reference for the method. + SetMethod(other.Method()); + } + + mirror::ArtMethod* Method() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + + void SetMethod(mirror::ArtMethod* m) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - void VisitRoots(RootCallback* callback, void* arg); + // Name 'Kind()' would collide with the above enum name. + Kind GetKind() const { + return kind_; + } - Kind kind; + void SetKind(Kind kind) { + kind_ = kind; + } + + uint32_t InstrumentationEvent() const { + return instrumentation_event_; + } + + void SetInstrumentationEvent(uint32_t instrumentation_event) { + instrumentation_event_ = instrumentation_event; + } + + private: + Kind kind_; // TODO we could use a union to hold the instrumentation_event and the method since they // respectively have sense only for kRegisterForEvent/kUnregisterForEvent and // kSelectiveDeoptimization/kSelectiveUndeoptimization. // Event to start or stop listening to. Only for kRegisterForEvent and kUnregisterForEvent. - uint32_t instrumentation_event; + uint32_t instrumentation_event_; // Method for selective deoptimization. - mirror::ArtMethod* method; + jmethodID method_; }; class Dbg { diff --git a/runtime/gc/space/image_space.cc b/runtime/gc/space/image_space.cc index 61633cd489..a87aa890c8 100644 --- a/runtime/gc/space/image_space.cc +++ b/runtime/gc/space/image_space.cc @@ -61,13 +61,6 @@ static bool GenerateImage(const std::string& image_filename, std::string* error_ image_option_string += image_filename; arg_vector.push_back(image_option_string); - arg_vector.push_back("--runtime-arg"); - arg_vector.push_back("-Xms64m"); - - arg_vector.push_back("--runtime-arg"); - arg_vector.push_back("-Xmx64m"); - - for (size_t i = 0; i < boot_class_path.size(); i++) { arg_vector.push_back(std::string("--dex-file=") + boot_class_path[i]); } @@ -348,6 +341,10 @@ bool ImageSpace::ValidateOatFile(std::string* error_msg) const { return true; } +const OatFile* ImageSpace::GetOatFile() const { + return oat_file_.get(); +} + OatFile* ImageSpace::ReleaseOatFile() { CHECK(oat_file_.get() != NULL); return oat_file_.release(); diff --git a/runtime/gc/space/image_space.h b/runtime/gc/space/image_space.h index 372db3a580..dd9b58084d 100644 --- a/runtime/gc/space/image_space.h +++ b/runtime/gc/space/image_space.h @@ -51,6 +51,9 @@ class ImageSpace : public MemMapSpace { static ImageHeader* ReadImageHeaderOrDie(const char* image_location, InstructionSet image_isa); + // Give access to the OatFile. + const OatFile* GetOatFile() const; + // Releases the OatFile from the ImageSpace so it can be transfer to // the caller, presumably the ClassLinker. OatFile* ReleaseOatFile() diff --git a/runtime/implicit_check_options.h b/runtime/implicit_check_options.h new file mode 100644 index 0000000000..b9ff0ac5ab --- /dev/null +++ b/runtime/implicit_check_options.h @@ -0,0 +1,118 @@ +/* + * Copyright (C) 2014 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. + */ + +#ifndef ART_RUNTIME_IMPLICIT_CHECK_OPTIONS_H_ +#define ART_RUNTIME_IMPLICIT_CHECK_OPTIONS_H_ + +#include "gc/heap.h" +#include "gc/space/image_space.h" +#include "instruction_set.h" +#include "runtime.h" + +#include <string> + +namespace art { + +class ImplicitCheckOptions { + public: + static constexpr const char* kImplicitChecksOatHeaderKey = "implicit-checks"; + + static std::string Serialize(bool explicit_null_checks, bool explicit_stack_overflow_checks, + bool explicit_suspend_checks) { + char tmp[4]; + tmp[0] = explicit_null_checks ? 'N' : 'n'; + tmp[1] = explicit_stack_overflow_checks ? 'O' : 'o'; + tmp[2] = explicit_suspend_checks ? 'S' : 's'; + tmp[3] = 0; + return std::string(tmp); + } + + static bool Parse(const char* str, bool* explicit_null_checks, + bool* explicit_stack_overflow_checks, bool* explicit_suspend_checks) { + if (str != nullptr && str[0] != 0 && str[1] != 0 && str[2] != 0 && + (str[0] == 'n' || str[0] == 'N') && + (str[1] == 'o' || str[1] == 'O') && + (str[2] == 's' || str[2] == 'S')) { + *explicit_null_checks = str[0] == 'N'; + *explicit_stack_overflow_checks = str[1] == 'O'; + *explicit_suspend_checks = str[2] == 'S'; + return true; + } else { + return false; + } + } + + static void Check(InstructionSet isa, bool* explicit_null_checks, + bool* explicit_stack_overflow_checks, bool* explicit_suspend_checks) { + switch (isa) { + case kArm: + case kThumb2: + break; // All checks implemented, leave as is. + + default: // No checks implemented, reset all to explicit checks. + *explicit_null_checks = true; + *explicit_stack_overflow_checks = true; + *explicit_suspend_checks = true; + } + } + + static bool CheckForCompiling(InstructionSet host, InstructionSet target, + bool* explicit_null_checks, bool* explicit_stack_overflow_checks, + bool* explicit_suspend_checks) { + // Check the boot image settings. + Runtime* runtime = Runtime::Current(); + if (runtime != nullptr) { + gc::space::ImageSpace* ispace = runtime->GetHeap()->GetImageSpace(); + if (ispace != nullptr) { + const OatFile* oat_file = ispace->GetOatFile(); + if (oat_file != nullptr) { + const char* v = oat_file->GetOatHeader().GetStoreValueByKey(kImplicitChecksOatHeaderKey); + if (!Parse(v, explicit_null_checks, explicit_stack_overflow_checks, + explicit_suspend_checks)) { + LOG(FATAL) << "Should have been able to parse boot image implicit check values"; + } + return true; + } + } + } + + // Check the current runtime. + bool cross_compiling = true; + switch (host) { + case kArm: + case kThumb2: + cross_compiling = target != kArm && target != kThumb2; + break; + default: + cross_compiling = host != target; + break; + } + if (!cross_compiling) { + Runtime* runtime = Runtime::Current(); + *explicit_null_checks = runtime->ExplicitNullChecks(); + *explicit_stack_overflow_checks = runtime->ExplicitStackOverflowChecks(); + *explicit_suspend_checks = runtime->ExplicitSuspendChecks(); + return true; + } + + // Give up. + return false; + } +}; + +} // namespace art + +#endif // ART_RUNTIME_IMPLICIT_CHECK_OPTIONS_H_ diff --git a/runtime/jdwp/jdwp_event.cc b/runtime/jdwp/jdwp_event.cc index 86c84e8b0f..36fbed4ea2 100644 --- a/runtime/jdwp/jdwp_event.cc +++ b/runtime/jdwp/jdwp_event.cc @@ -192,17 +192,17 @@ JdwpError JdwpState::RegisterEvent(JdwpEvent* pEvent) { } } if (NeedsFullDeoptimization(pEvent->eventKind)) { - CHECK_EQ(req.kind, DeoptimizationRequest::kNothing); - CHECK(req.method == nullptr); - req.kind = DeoptimizationRequest::kFullDeoptimization; + CHECK_EQ(req.GetKind(), DeoptimizationRequest::kNothing); + CHECK(req.Method() == nullptr); + req.SetKind(DeoptimizationRequest::kFullDeoptimization); } Dbg::RequestDeoptimization(req); } uint32_t instrumentation_event = GetInstrumentationEventFor(pEvent->eventKind); if (instrumentation_event != 0) { DeoptimizationRequest req; - req.kind = DeoptimizationRequest::kRegisterForEvent; - req.instrumentation_event = instrumentation_event; + req.SetKind(DeoptimizationRequest::kRegisterForEvent); + req.SetInstrumentationEvent(instrumentation_event); Dbg::RequestDeoptimization(req); } @@ -274,17 +274,17 @@ void JdwpState::UnregisterEvent(JdwpEvent* pEvent) { // deoptimization and only the last single-step will trigger a full undeoptimization. Dbg::DelayFullUndeoptimization(); } else if (NeedsFullDeoptimization(pEvent->eventKind)) { - CHECK_EQ(req.kind, DeoptimizationRequest::kNothing); - CHECK(req.method == nullptr); - req.kind = DeoptimizationRequest::kFullUndeoptimization; + CHECK_EQ(req.GetKind(), DeoptimizationRequest::kNothing); + CHECK(req.Method() == nullptr); + req.SetKind(DeoptimizationRequest::kFullUndeoptimization); } Dbg::RequestDeoptimization(req); } uint32_t instrumentation_event = GetInstrumentationEventFor(pEvent->eventKind); if (instrumentation_event != 0) { DeoptimizationRequest req; - req.kind = DeoptimizationRequest::kUnregisterForEvent; - req.instrumentation_event = instrumentation_event; + req.SetKind(DeoptimizationRequest::kUnregisterForEvent); + req.SetInstrumentationEvent(instrumentation_event); Dbg::RequestDeoptimization(req); } diff --git a/runtime/monitor.cc b/runtime/monitor.cc index 999a9e504b..eb62a694e0 100644 --- a/runtime/monitor.cc +++ b/runtime/monitor.cc @@ -401,8 +401,8 @@ void Monitor::Wait(Thread* self, int64_t ms, int32_t ns, // Make sure that we hold the lock. if (owner_ != self) { - ThrowIllegalMonitorStateExceptionF("object not locked by thread before wait()"); monitor_lock_.Unlock(self); + ThrowIllegalMonitorStateExceptionF("object not locked by thread before wait()"); return; } @@ -414,10 +414,10 @@ void Monitor::Wait(Thread* self, int64_t ms, int32_t ns, // Enforce the timeout range. if (ms < 0 || ns < 0 || ns > 999999) { + monitor_lock_.Unlock(self); ThrowLocation throw_location = self->GetCurrentLocationForThrow(); self->ThrowNewExceptionF(throw_location, "Ljava/lang/IllegalArgumentException;", "timeout arguments out of range: ms=%" PRId64 " ns=%d", ms, ns); - monitor_lock_.Unlock(self); return; } @@ -512,6 +512,8 @@ void Monitor::Wait(Thread* self, int64_t ms, int32_t ns, --num_waiters_; RemoveFromWaitSet(self); + monitor_lock_.Unlock(self); + if (was_interrupted) { /* * We were interrupted while waiting, or somebody interrupted an @@ -529,7 +531,6 @@ void Monitor::Wait(Thread* self, int64_t ms, int32_t ns, self->ThrowNewException(throw_location, "Ljava/lang/InterruptedException;", NULL); } } - monitor_lock_.Unlock(self); } void Monitor::Notify(Thread* self) { diff --git a/runtime/monitor_test.cc b/runtime/monitor_test.cc new file mode 100644 index 0000000000..bdba494e14 --- /dev/null +++ b/runtime/monitor_test.cc @@ -0,0 +1,380 @@ +/* + * Copyright (C) 2014 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 "barrier.h" +#include "monitor.h" + +#include <string> + +#include "atomic.h" +#include "common_runtime_test.h" +#include "handle_scope-inl.h" +#include "mirror/class-inl.h" +#include "mirror/string-inl.h" // Strings are easiest to allocate +#include "thread_pool.h" +#include "utils.h" + +namespace art { + +class MonitorTest : public CommonRuntimeTest { + protected: + void SetUpRuntimeOptions(Runtime::Options *options) OVERRIDE { + // Use a smaller heap + for (std::pair<std::string, const void*>& pair : *options) { + if (pair.first.find("-Xmx") == 0) { + pair.first = "-Xmx4M"; // Smallest we can go. + } + } + options->push_back(std::make_pair("-Xint", nullptr)); + } + public: + std::unique_ptr<Monitor> monitor_; + Handle<mirror::String> object_; + Handle<mirror::String> second_object_; + Handle<mirror::String> watchdog_object_; + // One exception test is for waiting on another Thread's lock. This is used to race-free & + // loop-free pass + Thread* thread_; + std::unique_ptr<Barrier> barrier_; + std::unique_ptr<Barrier> complete_barrier_; + bool completed_; +}; + +// Fill the heap. +static const size_t kMaxHandles = 1000000; // Use arbitrary large amount for now. +static void FillHeap(Thread* self, ClassLinker* class_linker, + std::unique_ptr<StackHandleScope<kMaxHandles>>* hsp, + std::vector<Handle<mirror::Object>>* handles) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + Runtime::Current()->GetHeap()->SetIdealFootprint(1 * GB); + + hsp->reset(new StackHandleScope<kMaxHandles>(self)); + // Class java.lang.Object. + Handle<mirror::Class> c((*hsp)->NewHandle(class_linker->FindSystemClass(self, + "Ljava/lang/Object;"))); + // Array helps to fill memory faster. + Handle<mirror::Class> ca((*hsp)->NewHandle(class_linker->FindSystemClass(self, + "[Ljava/lang/Object;"))); + + // Start allocating with 128K + size_t length = 128 * KB / 4; + while (length > 10) { + Handle<mirror::Object> h((*hsp)->NewHandle<mirror::Object>( + mirror::ObjectArray<mirror::Object>::Alloc(self, ca.Get(), length / 4))); + if (self->IsExceptionPending() || h.Get() == nullptr) { + self->ClearException(); + + // Try a smaller length + length = length / 8; + // Use at most half the reported free space. + size_t mem = Runtime::Current()->GetHeap()->GetFreeMemory(); + if (length * 8 > mem) { + length = mem / 8; + } + } else { + handles->push_back(h); + } + } + + // Allocate simple objects till it fails. + while (!self->IsExceptionPending()) { + Handle<mirror::Object> h = (*hsp)->NewHandle<mirror::Object>(c->AllocObject(self)); + if (!self->IsExceptionPending() && h.Get() != nullptr) { + handles->push_back(h); + } + } + self->ClearException(); +} + +// Check that an exception can be thrown correctly. +// This test is potentially racy, but the timeout is long enough that it should work. + +class CreateTask : public Task { + public: + explicit CreateTask(MonitorTest* monitor_test, uint64_t initial_sleep, int64_t millis, + bool expected) : + monitor_test_(monitor_test), initial_sleep_(initial_sleep), millis_(millis), + expected_(expected) {} + + void Run(Thread* self) { + { + ScopedObjectAccess soa(self); + + monitor_test_->thread_ = self; // Pass the Thread. + monitor_test_->object_.Get()->MonitorEnter(self); // Lock the object. This should transition + LockWord lock_after = monitor_test_->object_.Get()->GetLockWord(false); // it to thinLocked. + LockWord::LockState new_state = lock_after.GetState(); + + // Cannot use ASSERT only, as analysis thinks we'll keep holding the mutex. + if (LockWord::LockState::kThinLocked != new_state) { + monitor_test_->object_.Get()->MonitorExit(self); // To appease analysis. + ASSERT_EQ(LockWord::LockState::kThinLocked, new_state); // To fail the test. + return; + } + + // Force a fat lock by running identity hashcode to fill up lock word. + monitor_test_->object_.Get()->IdentityHashCode(); + LockWord lock_after2 = monitor_test_->object_.Get()->GetLockWord(false); + LockWord::LockState new_state2 = lock_after2.GetState(); + + // Cannot use ASSERT only, as analysis thinks we'll keep holding the mutex. + if (LockWord::LockState::kFatLocked != new_state2) { + monitor_test_->object_.Get()->MonitorExit(self); // To appease analysis. + ASSERT_EQ(LockWord::LockState::kFatLocked, new_state2); // To fail the test. + return; + } + } // Need to drop the mutator lock to use the barrier. + + monitor_test_->barrier_->Wait(self); // Let the other thread know we're done. + + { + ScopedObjectAccess soa(self); + + // Give the other task a chance to do its thing. + NanoSleep(initial_sleep_ * 1000 * 1000); + + // Now try to Wait on the Monitor. + Monitor::Wait(self, monitor_test_->object_.Get(), millis_, 0, true, + ThreadState::kTimedWaiting); + + // Check the exception status against what we expect. + EXPECT_EQ(expected_, self->IsExceptionPending()); + if (expected_) { + self->ClearException(); + } + } + + monitor_test_->complete_barrier_->Wait(self); // Wait for test completion. + + { + ScopedObjectAccess soa(self); + monitor_test_->object_.Get()->MonitorExit(self); // Release the object. Appeases analysis. + } + } + + void Finalize() { + delete this; + } + + private: + MonitorTest* monitor_test_; + uint64_t initial_sleep_; + int64_t millis_; + bool expected_; +}; + + +class UseTask : public Task { + public: + UseTask(MonitorTest* monitor_test, uint64_t initial_sleep, int64_t millis, bool expected) : + monitor_test_(monitor_test), initial_sleep_(initial_sleep), millis_(millis), + expected_(expected) {} + + void Run(Thread* self) { + monitor_test_->barrier_->Wait(self); // Wait for the other thread to set up the monitor. + + { + ScopedObjectAccess soa(self); + + // Give the other task a chance to do its thing. + NanoSleep(initial_sleep_ * 1000 * 1000); + + Monitor::Wait(self, monitor_test_->object_.Get(), millis_, 0, true, + ThreadState::kTimedWaiting); + + // Check the exception status against what we expect. + EXPECT_EQ(expected_, self->IsExceptionPending()); + if (expected_) { + self->ClearException(); + } + } + + monitor_test_->complete_barrier_->Wait(self); // Wait for test completion. + } + + void Finalize() { + delete this; + } + + private: + MonitorTest* monitor_test_; + uint64_t initial_sleep_; + int64_t millis_; + bool expected_; +}; + +class InterruptTask : public Task { + public: + InterruptTask(MonitorTest* monitor_test, uint64_t initial_sleep, uint64_t millis) : + monitor_test_(monitor_test), initial_sleep_(initial_sleep), millis_(millis) {} + + void Run(Thread* self) { + monitor_test_->barrier_->Wait(self); // Wait for the other thread to set up the monitor. + + { + ScopedObjectAccess soa(self); + + // Give the other task a chance to do its thing. + NanoSleep(initial_sleep_ * 1000 * 1000); + + // Interrupt the other thread. + monitor_test_->thread_->Interrupt(self); + + // Give it some more time to get to the exception code. + NanoSleep(millis_ * 1000 * 1000); + + // Now try to Wait. + Monitor::Wait(self, monitor_test_->object_.Get(), 10, 0, true, + ThreadState::kTimedWaiting); + + // No check here, as depending on scheduling we may or may not fail. + if (self->IsExceptionPending()) { + self->ClearException(); + } + } + + monitor_test_->complete_barrier_->Wait(self); // Wait for test completion. + } + + void Finalize() { + delete this; + } + + private: + MonitorTest* monitor_test_; + uint64_t initial_sleep_; + uint64_t millis_; +}; + +class WatchdogTask : public Task { + public: + explicit WatchdogTask(MonitorTest* monitor_test) : monitor_test_(monitor_test) {} + + void Run(Thread* self) { + ScopedObjectAccess soa(self); + + monitor_test_->watchdog_object_.Get()->MonitorEnter(self); // Lock the object. + + monitor_test_->watchdog_object_.Get()->Wait(self, 30 * 1000, 0); // Wait for 30s, or being + // woken up. + + monitor_test_->watchdog_object_.Get()->MonitorExit(self); // Release the lock. + + if (!monitor_test_->completed_) { + LOG(FATAL) << "Watchdog timeout!"; + } + } + + void Finalize() { + delete this; + } + + private: + MonitorTest* monitor_test_; +}; + +static void CommonWaitSetup(MonitorTest* test, ClassLinker* class_linker, uint64_t create_sleep, + int64_t c_millis, bool c_expected, bool interrupt, uint64_t use_sleep, + int64_t u_millis, bool u_expected, const char* pool_name) { + // First create the object we lock. String is easiest. + StackHandleScope<3> hs(Thread::Current()); + { + ScopedObjectAccess soa(Thread::Current()); + test->object_ = hs.NewHandle(mirror::String::AllocFromModifiedUtf8(Thread::Current(), + "hello, world!")); + test->watchdog_object_ = hs.NewHandle(mirror::String::AllocFromModifiedUtf8(Thread::Current(), + "hello, world!")); + } + + // Create the barrier used to synchronize. + test->barrier_ = std::unique_ptr<Barrier>(new Barrier(2)); + test->complete_barrier_ = std::unique_ptr<Barrier>(new Barrier(3)); + test->completed_ = false; + + // Fill the heap. + std::unique_ptr<StackHandleScope<kMaxHandles>> hsp; + std::vector<Handle<mirror::Object>> handles; + { + Thread* self = Thread::Current(); + ScopedObjectAccess soa(self); + + // Our job: Fill the heap, then try Wait. + FillHeap(self, class_linker, &hsp, &handles); + + // Now release everything. + auto it = handles.begin(); + auto end = handles.end(); + + for ( ; it != end; ++it) { + it->Assign(nullptr); + } + } // Need to drop the mutator lock to allow barriers. + + Thread* self = Thread::Current(); + ThreadPool thread_pool(pool_name, 3); + thread_pool.AddTask(self, new CreateTask(test, create_sleep, c_millis, c_expected)); + if (interrupt) { + thread_pool.AddTask(self, new InterruptTask(test, use_sleep, static_cast<uint64_t>(u_millis))); + } else { + thread_pool.AddTask(self, new UseTask(test, use_sleep, u_millis, u_expected)); + } + thread_pool.AddTask(self, new WatchdogTask(test)); + thread_pool.StartWorkers(self); + + // Wait on completion barrier. + test->complete_barrier_->Wait(Thread::Current()); + test->completed_ = true; + + // Wake the watchdog. + { + Thread* self = Thread::Current(); + ScopedObjectAccess soa(self); + + test->watchdog_object_.Get()->MonitorEnter(self); // Lock the object. + test->watchdog_object_.Get()->NotifyAll(self); // Wake up waiting parties. + test->watchdog_object_.Get()->MonitorExit(self); // Release the lock. + } + + thread_pool.StopWorkers(self); +} + + +// First test: throwing an exception when trying to wait in Monitor with another thread. +TEST_F(MonitorTest, CheckExceptionsWait1) { + // Make the CreateTask wait 10ms, the UseTask wait 10ms. + // => The use task will get the lock first and get to self == owner check. + CommonWaitSetup(this, class_linker_, 10, 50, false, false, 2, 50, true, + "Monitor test thread pool 1"); +} + +// Second test: throwing an exception for invalid wait time. +TEST_F(MonitorTest, CheckExceptionsWait2) { + // Make the CreateTask wait 0ms, the UseTask wait 10ms. + // => The create task will get the lock first and get to ms >= 0 + CommonWaitSetup(this, class_linker_, 0, -1, true, false, 10, 50, true, + "Monitor test thread pool 2"); +} + +// Third test: throwing an interrupted-exception. +TEST_F(MonitorTest, CheckExceptionsWait3) { + // Make the CreateTask wait 0ms, then Wait for a long time. Make the InterruptTask wait 10ms, + // after which it will interrupt the create task and then wait another 10ms. + // => The create task will get to the interrupted-exception throw. + CommonWaitSetup(this, class_linker_, 0, 500, true, true, 10, 50, true, + "Monitor test thread pool 3"); +} + +} // namespace art diff --git a/runtime/oat.cc b/runtime/oat.cc index 857c0a24e2..1421baffcf 100644 --- a/runtime/oat.cc +++ b/runtime/oat.cc @@ -17,15 +17,46 @@ #include "oat.h" #include "utils.h" +#include <string.h> #include <zlib.h> namespace art { const uint8_t OatHeader::kOatMagic[] = { 'o', 'a', 't', '\n' }; -const uint8_t OatHeader::kOatVersion[] = { '0', '3', '6', '\0' }; +const uint8_t OatHeader::kOatVersion[] = { '0', '3', '7', '\0' }; + +static size_t ComputeOatHeaderSize(const SafeMap<std::string, std::string>* variable_data) { + size_t estimate = 0U; + if (variable_data != nullptr) { + SafeMap<std::string, std::string>::const_iterator it = variable_data->begin(); + SafeMap<std::string, std::string>::const_iterator end = variable_data->end(); + for ( ; it != end; ++it) { + estimate += it->first.length() + 1; + estimate += it->second.length() + 1; + } + } + return sizeof(OatHeader) + estimate; +} + +OatHeader* OatHeader::Create(InstructionSet instruction_set, + const InstructionSetFeatures& instruction_set_features, + const std::vector<const DexFile*>* dex_files, + uint32_t image_file_location_oat_checksum, + uint32_t image_file_location_oat_data_begin, + const SafeMap<std::string, std::string>* variable_data) { + // Estimate size of optional data. + size_t needed_size = ComputeOatHeaderSize(variable_data); + + // Reserve enough memory. + void* memory = operator new (needed_size); -OatHeader::OatHeader() { - memset(this, 0, sizeof(*this)); + // Create the OatHeader in-place. + return new (memory) OatHeader(instruction_set, + instruction_set_features, + dex_files, + image_file_location_oat_checksum, + image_file_location_oat_data_begin, + variable_data); } OatHeader::OatHeader(InstructionSet instruction_set, @@ -33,7 +64,7 @@ OatHeader::OatHeader(InstructionSet instruction_set, const std::vector<const DexFile*>* dex_files, uint32_t image_file_location_oat_checksum, uint32_t image_file_location_oat_data_begin, - const std::string& image_file_location) { + const SafeMap<std::string, std::string>* variable_data) { memcpy(magic_, kOatMagic, sizeof(kOatMagic)); memcpy(version_, kOatVersion, sizeof(kOatVersion)); @@ -56,9 +87,16 @@ OatHeader::OatHeader(InstructionSet instruction_set, image_file_location_oat_data_begin_ = image_file_location_oat_data_begin; UpdateChecksum(&image_file_location_oat_data_begin_, sizeof(image_file_location_oat_data_begin_)); - image_file_location_size_ = image_file_location.size(); - UpdateChecksum(&image_file_location_size_, sizeof(image_file_location_size_)); - UpdateChecksum(image_file_location.data(), image_file_location_size_); + // Flatten the map. Will also update variable_size_data_size_. + Flatten(variable_data); + + // Update checksum for variable data size. + UpdateChecksum(&key_value_store_size_, sizeof(key_value_store_size_)); + + // Update for data, if existing. + if (key_value_store_size_ > 0U) { + UpdateChecksum(&key_value_store_, key_value_store_size_); + } executable_offset_ = 0; interpreter_to_interpreter_bridge_offset_ = 0; @@ -327,20 +365,97 @@ uint32_t OatHeader::GetImageFileLocationOatDataBegin() const { return image_file_location_oat_data_begin_; } -uint32_t OatHeader::GetImageFileLocationSize() const { +uint32_t OatHeader::GetKeyValueStoreSize() const { CHECK(IsValid()); - return image_file_location_size_; + return key_value_store_size_; } -const uint8_t* OatHeader::GetImageFileLocationData() const { +const uint8_t* OatHeader::GetKeyValueStore() const { CHECK(IsValid()); - return image_file_location_data_; + return key_value_store_; } -std::string OatHeader::GetImageFileLocation() const { - CHECK(IsValid()); - return std::string(reinterpret_cast<const char*>(GetImageFileLocationData()), - GetImageFileLocationSize()); +// Advance start until it is either end or \0. +static const char* ParseString(const char* start, const char* end) { + while (start < end && *start != 0) { + start++; + } + return start; +} + +const char* OatHeader::GetStoreValueByKey(const char* key) const { + const char* ptr = reinterpret_cast<const char*>(&key_value_store_); + const char* end = ptr + key_value_store_size_; + + while (ptr < end) { + // Scan for a closing zero. + const char* str_end = ParseString(ptr, end); + if (str_end < end) { + if (strcmp(key, ptr) == 0) { + // Same as key. Check if value is OK. + if (ParseString(str_end + 1, end) < end) { + return str_end + 1; + } + } else { + // Different from key. Advance over the value. + ptr = ParseString(str_end + 1, end) + 1; + } + } else { + break; + } + } + // Not found. + return nullptr; +} + +bool OatHeader::GetStoreKeyValuePairByIndex(size_t index, const char** key, + const char** value) const { + const char* ptr = reinterpret_cast<const char*>(&key_value_store_); + const char* end = ptr + key_value_store_size_; + ssize_t counter = static_cast<ssize_t>(index); + + while (ptr < end && counter >= 0) { + // Scan for a closing zero. + const char* str_end = ParseString(ptr, end); + if (str_end < end) { + const char* maybe_key = ptr; + ptr = ParseString(str_end + 1, end) + 1; + if (ptr <= end) { + if (counter == 0) { + *key = maybe_key; + *value = str_end + 1; + return true; + } else { + counter--; + } + } else { + return false; + } + } else { + break; + } + } + // Not found. + return false; +} + +size_t OatHeader::GetHeaderSize() const { + return sizeof(OatHeader) + key_value_store_size_; +} + +void OatHeader::Flatten(const SafeMap<std::string, std::string>* key_value_store) { + char* data_ptr = reinterpret_cast<char*>(&key_value_store_); + if (key_value_store != nullptr) { + SafeMap<std::string, std::string>::const_iterator it = key_value_store->begin(); + SafeMap<std::string, std::string>::const_iterator end = key_value_store->end(); + for ( ; it != end; ++it) { + strcpy(data_ptr, it->first.c_str()); + data_ptr += it->first.length() + 1; + strcpy(data_ptr, it->second.c_str()); + data_ptr += it->second.length() + 1; + } + } + key_value_store_size_ = data_ptr - reinterpret_cast<char*>(&key_value_store_); } OatMethodOffsets::OatMethodOffsets() diff --git a/runtime/oat.h b/runtime/oat.h index 7be768c5cb..fbed596d33 100644 --- a/runtime/oat.h +++ b/runtime/oat.h @@ -23,6 +23,7 @@ #include "dex_file.h" #include "instruction_set.h" #include "quick/quick_method_frame_info.h" +#include "safe_map.h" namespace art { @@ -31,13 +32,16 @@ class PACKED(4) OatHeader { static const uint8_t kOatMagic[4]; static const uint8_t kOatVersion[4]; - OatHeader(); - OatHeader(InstructionSet instruction_set, - const InstructionSetFeatures& instruction_set_features, - const std::vector<const DexFile*>* dex_files, - uint32_t image_file_location_oat_checksum, - uint32_t image_file_location_oat_data_begin, - const std::string& image_file_location); + static constexpr const char* kImageLocationKey = "image-location"; + static constexpr const char* kDex2OatCmdLineKey = "dex2oat-cmdline"; + static constexpr const char* kDex2OatHostKey = "dex2oat-host"; + + static OatHeader* Create(InstructionSet instruction_set, + const InstructionSetFeatures& instruction_set_features, + const std::vector<const DexFile*>* dex_files, + uint32_t image_file_location_oat_checksum, + uint32_t image_file_location_oat_data_begin, + const SafeMap<std::string, std::string>* variable_data); bool IsValid() const; const char* GetMagic() const; @@ -88,11 +92,24 @@ class PACKED(4) OatHeader { const InstructionSetFeatures& GetInstructionSetFeatures() const; uint32_t GetImageFileLocationOatChecksum() const; uint32_t GetImageFileLocationOatDataBegin() const; - uint32_t GetImageFileLocationSize() const; - const uint8_t* GetImageFileLocationData() const; - std::string GetImageFileLocation() const; + + uint32_t GetKeyValueStoreSize() const; + const uint8_t* GetKeyValueStore() const; + const char* GetStoreValueByKey(const char* key) const; + bool GetStoreKeyValuePairByIndex(size_t index, const char** key, const char** value) const; + + size_t GetHeaderSize() const; private: + OatHeader(InstructionSet instruction_set, + const InstructionSetFeatures& instruction_set_features, + const std::vector<const DexFile*>* dex_files, + uint32_t image_file_location_oat_checksum, + uint32_t image_file_location_oat_data_begin, + const SafeMap<std::string, std::string>* variable_data); + + void Flatten(const SafeMap<std::string, std::string>* variable_data); + uint8_t magic_[4]; uint8_t version_[4]; uint32_t adler32_checksum_; @@ -114,8 +131,9 @@ class PACKED(4) OatHeader { uint32_t image_file_location_oat_checksum_; uint32_t image_file_location_oat_data_begin_; - uint32_t image_file_location_size_; - uint8_t image_file_location_data_[0]; // note variable width data at end + + uint32_t key_value_store_size_; + uint8_t key_value_store_[0]; // note variable width data at end DISALLOW_COPY_AND_ASSIGN(OatHeader); }; diff --git a/runtime/oat_file.cc b/runtime/oat_file.cc index 6c44aa91bd..bae1632fa2 100644 --- a/runtime/oat_file.cc +++ b/runtime/oat_file.cc @@ -17,17 +17,20 @@ #include "oat_file.h" #include <dlfcn.h> +#include <sstream> #include "base/bit_vector.h" #include "base/stl_util.h" #include "base/unix_file/fd_file.h" #include "elf_file.h" +#include "implicit_check_options.h" #include "oat.h" #include "mirror/art_method.h" #include "mirror/art_method-inl.h" #include "mirror/class.h" #include "mirror/object-inl.h" #include "os.h" +#include "runtime.h" #include "utils.h" #include "vmap_table.h" @@ -55,28 +58,71 @@ OatFile* OatFile::Open(const std::string& filename, std::string* error_msg) { CHECK(!filename.empty()) << location; CheckLocation(filename); - if (kUsePortableCompiler) { + std::unique_ptr<OatFile> ret; + if (kUsePortableCompiler && executable) { // If we are using PORTABLE, use dlopen to deal with relocations. // // We use our own ELF loader for Quick to deal with legacy apps that // open a generated dex file by name, remove the file, then open // another generated dex file with the same name. http://b/10614658 - if (executable) { - return OpenDlopen(filename, location, requested_base, error_msg); + ret.reset(OpenDlopen(filename, location, requested_base, error_msg)); + } else { + // If we aren't trying to execute, we just use our own ElfFile loader for a couple reasons: + // + // On target, dlopen may fail when compiling due to selinux restrictions on installd. + // + // On host, dlopen is expected to fail when cross compiling, so fall back to OpenElfFile. + // This won't work for portable runtime execution because it doesn't process relocations. + std::unique_ptr<File> file(OS::OpenFileForReading(filename.c_str())); + if (file.get() == NULL) { + *error_msg = StringPrintf("Failed to open oat filename for reading: %s", strerror(errno)); + return nullptr; } + ret.reset(OpenElfFile(file.get(), location, requested_base, false, executable, error_msg)); + } + + if (ret.get() == nullptr) { + return nullptr; + } + + // Embedded options check. Right now only implicit checks. + // TODO: Refactor to somewhere else? + const char* implicit_checks_value = ret->GetOatHeader(). + GetStoreValueByKey(ImplicitCheckOptions::kImplicitChecksOatHeaderKey); + + if (implicit_checks_value == nullptr) { + *error_msg = "Did not find implicit checks value."; + return nullptr; } - // If we aren't trying to execute, we just use our own ElfFile loader for a couple reasons: - // - // On target, dlopen may fail when compiling due to selinux restrictions on installd. - // - // On host, dlopen is expected to fail when cross compiling, so fall back to OpenElfFile. - // This won't work for portable runtime execution because it doesn't process relocations. - std::unique_ptr<File> file(OS::OpenFileForReading(filename.c_str())); - if (file.get() == NULL) { - *error_msg = StringPrintf("Failed to open oat filename for reading: %s", strerror(errno)); - return NULL; + + bool explicit_null_checks, explicit_so_checks, explicit_suspend_checks; + if (ImplicitCheckOptions::Parse(implicit_checks_value, &explicit_null_checks, + &explicit_so_checks, &explicit_suspend_checks)) { + if (!executable) { + // Not meant to be run, i.e., either we are compiling or dumping. Just accept. + return ret.release(); + } + + Runtime* runtime = Runtime::Current(); + // We really should have a runtime. + DCHECK_NE(static_cast<Runtime*>(nullptr), runtime); + + if (runtime->ExplicitNullChecks() != explicit_null_checks || + runtime->ExplicitStackOverflowChecks() != explicit_so_checks || + runtime->ExplicitSuspendChecks() != explicit_suspend_checks) { + std::ostringstream os; + os << "Explicit check options do not match runtime: " << implicit_checks_value << " -> "; + os << runtime->ExplicitNullChecks() << " vs " << explicit_null_checks << " | "; + os << runtime->ExplicitStackOverflowChecks() << " vs " << explicit_so_checks << " | "; + os << runtime->ExplicitSuspendChecks() << " vs " << explicit_suspend_checks; + *error_msg = os.str(); + return nullptr; + } + return ret.release(); + } else { + *error_msg = "Failed parsing implicit check options."; + return nullptr; } - return OpenElfFile(file.get(), location, requested_base, false, executable, error_msg); } OatFile* OatFile::OpenWritable(File* file, const std::string& location, std::string* error_msg) { @@ -206,11 +252,11 @@ bool OatFile::Setup(std::string* error_msg) { return false; } - oat += GetOatHeader().GetImageFileLocationSize(); + oat += GetOatHeader().GetKeyValueStoreSize(); if (oat > End()) { - *error_msg = StringPrintf("In oat file '%s' found truncated image file location: " + *error_msg = StringPrintf("In oat file '%s' found truncated variable-size data: " "%p + %zd + %ud <= %p", GetLocation().c_str(), - Begin(), sizeof(OatHeader), GetOatHeader().GetImageFileLocationSize(), + Begin(), sizeof(OatHeader), GetOatHeader().GetKeyValueStoreSize(), End()); return false; } diff --git a/runtime/runtime.cc b/runtime/runtime.cc index 53ddcca469..3b14aaa767 100644 --- a/runtime/runtime.cc +++ b/runtime/runtime.cc @@ -930,7 +930,6 @@ void Runtime::VisitConstantRoots(RootCallback* callback, void* arg) { void Runtime::VisitConcurrentRoots(RootCallback* callback, void* arg, VisitRootFlags flags) { intern_table_->VisitRoots(callback, arg, flags); class_linker_->VisitRoots(callback, arg, flags); - Dbg::VisitRoots(callback, arg); if ((flags & kVisitRootFlagNewRoots) == 0) { // Guaranteed to have no new roots in the constant roots. VisitConstantRoots(callback, arg); diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc index 89cfcdd1de..eabb993879 100644 --- a/runtime/verifier/method_verifier.cc +++ b/runtime/verifier/method_verifier.cc @@ -46,7 +46,7 @@ namespace art { namespace verifier { -static const bool gDebugVerify = false; +static constexpr bool gDebugVerify = false; // TODO: Add a constant to method_verifier to turn on verbose logging? void PcToRegisterLineTable::Init(RegisterTrackingMode mode, InstructionFlags* flags, @@ -1329,8 +1329,7 @@ bool MethodVerifier::CodeFlowVerifyMethod() { work_insn_idx_ = insn_idx; if (insn_flags_[insn_idx].IsBranchTarget()) { work_line_->CopyFromLine(reg_table_.GetLine(insn_idx)); - } else { -#ifndef NDEBUG + } else if (kIsDebugBuild) { /* * Sanity check: retrieve the stored register line (assuming * a full table) and make sure it actually matches. @@ -1346,7 +1345,6 @@ bool MethodVerifier::CodeFlowVerifyMethod() { << " expected=" << *register_line; } } -#endif } if (!CodeFlowVerifyInstruction(&start_guess)) { std::string prepend(PrettyMethod(dex_method_idx_, *dex_file_)); @@ -1958,14 +1956,24 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { (Instruction::INSTANCE_OF == instance_of_inst->Opcode()) && (inst->VRegA_21t() == instance_of_inst->VRegA_22c()) && (instance_of_inst->VRegA_22c() != instance_of_inst->VRegB_22c())) { - // Check that the we are not attempting conversion to interface types, - // which is not done because of the multiple inheritance implications. - // Also don't change the type if it would result in an upcast. + // Check the type of the instance-of is different than that of registers type, as if they + // are the same there is no work to be done here. Check that the conversion is not to or + // from an unresolved type as type information is imprecise. If the instance-of is to an + // interface then ignore the type information as interfaces can only be treated as Objects + // and we don't want to disallow field and other operations on the object. If the value + // being instance-of checked against is known null (zero) then allow the optimization as + // we didn't have type information. If the merge of the instance-of type with the original + // type is assignable to the original then allow optimization. This check is performed to + // ensure that subsequent merges don't lose type information - such as becoming an + // interface from a class that would lose information relevant to field checks. const RegType& orig_type = work_line_->GetRegisterType(instance_of_inst->VRegB_22c()); const RegType& cast_type = ResolveClassAndCheckAccess(instance_of_inst->VRegC_22c()); - if (!cast_type.IsUnresolvedTypes() && !orig_type.IsUnresolvedTypes() && - !cast_type.GetClass()->IsInterface() && !cast_type.IsAssignableFrom(orig_type)) { + if (!orig_type.Equals(cast_type) && + !cast_type.IsUnresolvedTypes() && !orig_type.IsUnresolvedTypes() && + !cast_type.GetClass()->IsInterface() && + (orig_type.IsZero() || + orig_type.IsStrictlyAssignableFrom(cast_type.Merge(orig_type, ®_types_)))) { RegisterLine* update_line = RegisterLine::Create(code_item_->registers_size_, this); if (inst->Opcode() == Instruction::IF_EQZ) { fallthrough_line.reset(update_line); @@ -2699,11 +2707,11 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { } /* update branch target, set "changed" if appropriate */ if (NULL != branch_line.get()) { - if (!UpdateRegisters(work_insn_idx_ + branch_target, branch_line.get())) { + if (!UpdateRegisters(work_insn_idx_ + branch_target, branch_line.get(), false)) { return false; } } else { - if (!UpdateRegisters(work_insn_idx_ + branch_target, work_line_.get())) { + if (!UpdateRegisters(work_insn_idx_ + branch_target, work_line_.get(), false)) { return false; } } @@ -2743,8 +2751,9 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { if (!CheckNotMoveException(code_item_->insns_, abs_offset)) { return false; } - if (!UpdateRegisters(abs_offset, work_line_.get())) + if (!UpdateRegisters(abs_offset, work_line_.get(), false)) { return false; + } } } @@ -2765,7 +2774,7 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { * "work_regs", because at runtime the exception will be thrown before the instruction * modifies any registers. */ - if (!UpdateRegisters(iterator.GetHandlerAddress(), saved_line_.get())) { + if (!UpdateRegisters(iterator.GetHandlerAddress(), saved_line_.get(), false)) { return false; } } @@ -2824,9 +2833,10 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { } RegisterLine* next_line = reg_table_.GetLine(next_insn_idx); if (next_line != NULL) { - // Merge registers into what we have for the next instruction, - // and set the "changed" flag if needed. - if (!UpdateRegisters(next_insn_idx, work_line_.get())) { + // Merge registers into what we have for the next instruction, and set the "changed" flag if + // needed. If the merge changes the state of the registers then the work line will be + // updated. + if (!UpdateRegisters(next_insn_idx, work_line_.get(), true)) { return false; } } else { @@ -3890,7 +3900,8 @@ bool MethodVerifier::CheckNotMoveException(const uint16_t* insns, int insn_idx) return true; } -bool MethodVerifier::UpdateRegisters(uint32_t next_insn, const RegisterLine* merge_line) { +bool MethodVerifier::UpdateRegisters(uint32_t next_insn, RegisterLine* merge_line, + bool update_merge_line) { bool changed = true; RegisterLine* target_line = reg_table_.GetLine(next_insn); if (!insn_flags_[next_insn].IsVisitedOrChanged()) { @@ -3939,6 +3950,9 @@ bool MethodVerifier::UpdateRegisters(uint32_t next_insn, const RegisterLine* mer << *merge_line << " ==\n" << *target_line << "\n"; } + if (update_merge_line && changed) { + merge_line->CopyFromLine(target_line); + } } if (changed) { insn_flags_[next_insn].SetChanged(); diff --git a/runtime/verifier/method_verifier.h b/runtime/verifier/method_verifier.h index b6d5b351c3..757c41993c 100644 --- a/runtime/verifier/method_verifier.h +++ b/runtime/verifier/method_verifier.h @@ -595,9 +595,11 @@ class MethodVerifier { /* * Control can transfer to "next_insn". Merge the registers from merge_line into the table at * next_insn, and set the changed flag on the target address if any of the registers were changed. + * In the case of fall-through, update the merge line on a change as its the working line for the + * next instruction. * Returns "false" if an error is encountered. */ - bool UpdateRegisters(uint32_t next_insn, const RegisterLine* merge_line) + bool UpdateRegisters(uint32_t next_insn, RegisterLine* merge_line, bool update_merge_line) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); // Is the method being verified a constructor? diff --git a/runtime/verifier/reg_type.h b/runtime/verifier/reg_type.h index 64001d36f3..e985f3a2de 100644 --- a/runtime/verifier/reg_type.h +++ b/runtime/verifier/reg_type.h @@ -209,9 +209,9 @@ class RegType { !IsUnresolvedSuperClass())); return descriptor_; } - mirror::Class* GetClass() const { + mirror::Class* GetClass() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { DCHECK(!IsUnresolvedReference()); - DCHECK(klass_ != NULL); + DCHECK(klass_ != NULL) << Dump(); DCHECK(HasClass()); return klass_; } |