diff options
Diffstat (limited to 'src')
47 files changed, 1018 insertions, 298 deletions
diff --git a/src/class_linker.cc b/src/class_linker.cc index 3437a5a8e0..a7d10f4a13 100644 --- a/src/class_linker.cc +++ b/src/class_linker.cc @@ -257,7 +257,7 @@ Method* ClassLinker::AllocMethod() { return down_cast<Method*>(GetClassRoot(kJavaLangReflectMethod)->NewInstance()); } -// TODO remove once we can use java.lang.Class.getSystemClassLoader +// TODO: remove once we can use java.lang.Class.getSystemClassLoader PathClassLoader* ClassLinker::AllocPathClassLoader(std::vector<const DexFile*> dex_files) { PathClassLoader* cl = down_cast<PathClassLoader*>(GetClassRoot(kDalvikSystemPathClassLoader)->NewInstance()); cl->SetClassPath(dex_files); @@ -266,7 +266,7 @@ PathClassLoader* ClassLinker::AllocPathClassLoader(std::vector<const DexFile*> d Class* ClassLinker::FindClass(const StringPiece& descriptor, ClassLoader* class_loader) { - // TODO remove this contrived parent class loader check when we have a real ClassLoader. + // TODO: remove this contrived parent class loader check when we have a real ClassLoader. if (class_loader != NULL) { Class* klass = FindClass(descriptor, NULL); if (klass != NULL) { @@ -323,7 +323,7 @@ Class* ClassLinker::FindClass(const StringPiece& descriptor, ObjectLock lock(klass); klass->clinit_thread_id_ = self->GetId(); // Add the newly loaded class to the loaded classes table. - bool success = InsertClass(klass); // TODO just return collision + bool success = InsertClass(klass); // TODO: just return collision if (!success) { // We may fail to insert if we raced with another thread. klass->clinit_thread_id_ = 0; diff --git a/src/class_linker.h b/src/class_linker.h index b2b4c950d6..34ad9d7d3a 100644 --- a/src/class_linker.h +++ b/src/class_linker.h @@ -203,7 +203,7 @@ class ClassLinker { bool init_done_; - friend class RuntimeTest; + friend class CommonTest; FRIEND_TEST(DexCacheTest, Open); friend class ObjectTest; FRIEND_TEST(ObjectTest, AllocObjectArray); diff --git a/src/class_linker_test.cc b/src/class_linker_test.cc index 3b70aa928a..21c576ecf8 100644 --- a/src/class_linker_test.cc +++ b/src/class_linker_test.cc @@ -9,7 +9,7 @@ namespace art { -class ClassLinkerTest : public RuntimeTest { +class ClassLinkerTest : public CommonTest { protected: void AssertNonExistantClass(const StringPiece& descriptor) { EXPECT_TRUE(class_linker_->FindSystemClass(descriptor) == NULL); @@ -394,59 +394,59 @@ TEST_F(ClassLinkerTest, StaticFields) { scoped_ptr<DexFile> dex(OpenDexFileBase64(kStatics)); PathClassLoader* class_loader = AllocPathClassLoader(dex.get()); Class* statics = class_linker_->FindClass("LStatics;", class_loader); - // class_linker_->InitializeClass(statics); // TODO uncomment this + // class_linker_->InitializeClass(statics); // TODO: uncomment this EXPECT_EQ(10U, statics->NumStaticFields()); Field* s0 = statics->GetStaticField(0); EXPECT_EQ("Ljava/lang/reflect/Field;", s0->GetClass()->descriptor_); EXPECT_EQ('Z', s0->GetType()); -// EXPECT_EQ(true, s0->GetBoolean()); // TODO uncomment this +// EXPECT_EQ(true, s0->GetBoolean()); // TODO: uncomment this s0->SetBoolean(false); Field* s1 = statics->GetStaticField(1); EXPECT_EQ('B', s1->GetType()); -// EXPECT_EQ(5, s1->GetByte()); // TODO uncomment this +// EXPECT_EQ(5, s1->GetByte()); // TODO: uncomment this s1->SetByte(6); Field* s2 = statics->GetStaticField(2); EXPECT_EQ('C', s2->GetType()); -// EXPECT_EQ('a', s2->GetChar()); // TODO uncomment this +// EXPECT_EQ('a', s2->GetChar()); // TODO: uncomment this s2->SetChar('b'); Field* s3 = statics->GetStaticField(3); EXPECT_EQ('S', s3->GetType()); -// EXPECT_EQ(65000, s3->GetShort()); // TODO uncomment this +// EXPECT_EQ(65000, s3->GetShort()); // TODO: uncomment this s3->SetShort(65001); Field* s4 = statics->GetStaticField(4); EXPECT_EQ('I', s4->GetType()); -// EXPECT_EQ(2000000000, s4->GetInt()); // TODO uncomment this +// EXPECT_EQ(2000000000, s4->GetInt()); // TODO: uncomment this s4->SetInt(2000000001); Field* s5 = statics->GetStaticField(5); EXPECT_EQ('J', s5->GetType()); -// EXPECT_EQ(0x1234567890abcdefLL, s5->GetLong()); // TODO uncomment this +// EXPECT_EQ(0x1234567890abcdefLL, s5->GetLong()); // TODO: uncomment this s5->SetLong(0x34567890abcdef12LL); Field* s6 = statics->GetStaticField(6); EXPECT_EQ('F', s6->GetType()); -// EXPECT_EQ(0.5, s6->GetFloat()); // TODO uncomment this +// EXPECT_EQ(0.5, s6->GetFloat()); // TODO: uncomment this s6->SetFloat(0.75); Field* s7 = statics->GetStaticField(7); EXPECT_EQ('D', s7->GetType()); -// EXPECT_EQ(16777217, s7->GetDouble()); // TODO uncomment this +// EXPECT_EQ(16777217, s7->GetDouble()); // TODO: uncomment this s7->SetDouble(16777219); Field* s8 = statics->GetStaticField(8); EXPECT_EQ('L', s8->GetType()); -// EXPECT_TRUE(down_cast<String*>(s8->GetObject())->Equals("android")); // TODO uncomment this +// EXPECT_TRUE(down_cast<String*>(s8->GetObject())->Equals("android")); // TODO: uncomment this s8->SetObject(String::AllocFromAscii("robot")); Field* s9 = statics->GetStaticField(9); EXPECT_EQ('[', s9->GetType()); -// EXPECT_EQ(NULL, s9->GetObject()); // TODO uncomment this +// EXPECT_EQ(NULL, s9->GetObject()); // TODO: uncomment this s9->SetObject(NULL); EXPECT_EQ(false, s0->GetBoolean()); diff --git a/src/common_test.h b/src/common_test.h index 1fbe718ff8..14a68a2de0 100644 --- a/src/common_test.h +++ b/src/common_test.h @@ -1,6 +1,7 @@ // Copyright 2011 Google Inc. All Rights Reserved. #include <dirent.h> +#include <dlfcn.h> #include <sys/stat.h> #include <sys/types.h> @@ -11,6 +12,9 @@ #include "class_linker.h" #include "dex_file.h" +#include "unicode/uclean.h" +#include "unicode/uvernum.h" + #include "gtest/gtest.h" namespace art { @@ -315,11 +319,20 @@ class ScratchFile { int fd_; }; -class RuntimeTest : public testing::Test { +class CommonTest : public testing::Test { protected: virtual void SetUp() { is_host_ = getenv("ANDROID_BUILD_TOP") != NULL; + if (is_host_) { + // $ANDROID_ROOT is set on the device, but not on the host. + // We need to set this so that icu4c can find its locale data. + std::string root; + root += getenv("ANDROID_BUILD_TOP"); + root += "/out/host/linux-x86"; + setenv("ANDROID_ROOT", root.c_str(), 1); + } + android_data_.reset(strdup(is_host_ ? "/tmp/art-data-XXXXXX" : "/sdcard/art-data-XXXXXX")); ASSERT_TRUE(android_data_ != NULL); const char* android_data_modified = mkdtemp(android_data_.get()); @@ -368,6 +381,15 @@ class RuntimeTest : public testing::Test { ASSERT_EQ(0, rmdir_cache_result); int rmdir_data_result = rmdir(android_data_.get()); ASSERT_EQ(0, rmdir_data_result); + + // icu4c has a fixed 10-element array "gCommonICUDataArray". + // If we run > 10 tests, we fill that array and u_setCommonData fails. + // There's a function to clear the array, but it's not public... + typedef void (*IcuCleanupFn)(); + void* sym = dlsym(RTLD_DEFAULT, "u_cleanup_" U_ICU_VERSION_SHORT); + CHECK(sym != NULL); + IcuCleanupFn icu_cleanup_fn = reinterpret_cast<IcuCleanupFn>(sym); + (*icu_cleanup_fn)(); } std::string GetLibCoreDexFileName() { @@ -394,7 +416,7 @@ class RuntimeTest : public testing::Test { bool is_host_; scoped_ptr_malloc<char> android_data_; std::string art_cache_; - scoped_ptr<DexFile> java_lang_dex_file_; + scoped_ptr<const DexFile> java_lang_dex_file_; scoped_ptr<Runtime> runtime_; ClassLinker* class_linker_; }; diff --git a/src/dex_cache_test.cc b/src/dex_cache_test.cc index c439311f25..cb682a08df 100644 --- a/src/dex_cache_test.cc +++ b/src/dex_cache_test.cc @@ -12,7 +12,7 @@ namespace art { -class DexCacheTest : public RuntimeTest {}; +class DexCacheTest : public CommonTest {}; TEST_F(DexCacheTest, Open) { diff --git a/src/dex_file.cc b/src/dex_file.cc index 22e4430d46..b9ae7e3b1a 100644 --- a/src/dex_file.cc +++ b/src/dex_file.cc @@ -35,7 +35,7 @@ DexFile::ClassPathEntry DexFile::FindInClassPath(const StringPiece& descriptor, return ClassPathEntry(dex_file, dex_class_def); } } - // TODO remove reinterpret_cast when issue with -std=gnu++0x host issue resolved + // TODO: remove reinterpret_cast when issue with -std=gnu++0x host issue resolved return ClassPathEntry(reinterpret_cast<const DexFile*>(NULL), reinterpret_cast<const DexFile::ClassDef*>(NULL)); } @@ -257,7 +257,7 @@ DexFile* DexFile::OpenZip(const std::string& filename) { return NULL; } - // TODO restat and check length against zip_entry->GetUncompressedLength()? + // TODO: restat and check length against zip_entry->GetUncompressedLength()? // Compute checksum and compare to zip. If things look okay, rename from tmp. off_t lseek_result = lseek(fd->GetFd(), 0, SEEK_SET); diff --git a/src/exception_test.cc b/src/exception_test.cc index ae1546f331..e2f97c0a9c 100644 --- a/src/exception_test.cc +++ b/src/exception_test.cc @@ -60,7 +60,7 @@ static const char kMyClassExceptionHandleDex[] = "AQAAABgCAAABEAAAAQAAADgCAAACIAAADQAAAD4CAAADIAAABAAAAN4CAAAEIAAAAQAAAAEDAAAA" "IAAAAgAAAAkDAAAAEAAAAQAAACgDAAA="; -class ExceptionTest : public RuntimeTest { +class ExceptionTest : public CommonTest { }; TEST_F(ExceptionTest, MyClass_F_G) { diff --git a/src/file.h b/src/file.h index 680872e776..3353602145 100644 --- a/src/file.h +++ b/src/file.h @@ -33,6 +33,8 @@ class File { // Returns a negative value if position cannot be determined. virtual off_t Position() = 0; + virtual int Fd() = 0; + const char* name() const { return name_; } protected: diff --git a/src/file_linux.h b/src/file_linux.h index 7e36adaf94..3d381d545e 100644 --- a/src/file_linux.h +++ b/src/file_linux.h @@ -22,6 +22,10 @@ class LinuxFile : public File { virtual off_t Length(); virtual off_t Position(); + virtual int Fd() { + return fd_; + } + private: static const int kClosedFd = -1; diff --git a/src/file_test.cc b/src/file_test.cc index 4afc834d8b..385b3dd647 100644 --- a/src/file_test.cc +++ b/src/file_test.cc @@ -9,7 +9,7 @@ namespace art { -class FileTest : public RuntimeTest {}; +class FileTest : public CommonTest {}; TEST_F(FileTest, Read) { std::string filename = GetLibCoreDexFileName(); @@ -44,4 +44,13 @@ TEST_F(FileTest, FilePosition) { EXPECT_EQ(4, file->Position()); } + +TEST_F(FileTest, FileFd) { + std::string filename = GetLibCoreDexFileName(); + scoped_ptr<File> file(OS::OpenFile(filename.c_str(), false)); + ASSERT_TRUE(file != NULL); + EXPECT_NE(-1, file->Fd()); +} + + } // namespace art diff --git a/src/heap.cc b/src/heap.cc index b9998dae92..de801cebc0 100644 --- a/src/heap.cc +++ b/src/heap.cc @@ -14,7 +14,7 @@ namespace art { std::vector<Space*> Heap::spaces_; -size_t Heap::startup_size_ = 0; +Space* Heap::alloc_space_ = NULL; size_t Heap::maximum_size_ = 0; @@ -28,14 +28,33 @@ HeapBitmap* Heap::mark_bitmap_ = NULL; HeapBitmap* Heap::live_bitmap_ = NULL; -bool Heap::Init(size_t startup_size, size_t maximum_size) { - Space* space = Space::Create(startup_size, maximum_size); +bool Heap::Init(size_t initial_size, size_t maximum_size, const char* boot_image_file_name) { + Space* boot_space; + byte* requested_base; + if (boot_image_file_name == NULL) { + boot_space = NULL; + requested_base = NULL; + } else { + boot_space = Space::Create(boot_image_file_name); + if (boot_space == NULL) { + return false; + } + spaces_.push_back(boot_space); + requested_base = boot_space->GetBase() + RoundUp(boot_space->Size(), kPageSize); + } + + Space* space = Space::Create(initial_size, maximum_size, requested_base); if (space == NULL) { return false; } - byte* base = space->GetBase(); - size_t num_bytes = space->Size(); + if (boot_space == NULL) { + boot_space = space; + } + byte* base = std::min(boot_space->GetBase(), space->GetBase()); + byte* limit = std::max(boot_space->GetLimit(), space->GetLimit()); + DCHECK_LT(base, limit); + size_t num_bytes = limit - base; // Allocate the initial live bitmap. scoped_ptr<HeapBitmap> live_bitmap(HeapBitmap::Create(base, num_bytes)); @@ -49,8 +68,8 @@ bool Heap::Init(size_t startup_size, size_t maximum_size) { return false; } + alloc_space_ = space; spaces_.push_back(space); - startup_size_ = startup_size; maximum_size_ = maximum_size; live_bitmap_ = live_bitmap.release(); mark_bitmap_ = mark_bitmap.release(); @@ -62,8 +81,14 @@ bool Heap::Init(size_t startup_size, size_t maximum_size) { void Heap::Destroy() { STLDeleteElements(&spaces_); - delete mark_bitmap_; - delete live_bitmap_; + if (mark_bitmap_ != NULL) { + delete mark_bitmap_; + mark_bitmap_ = NULL; + } + if (live_bitmap_ != NULL) { + delete live_bitmap_; + } + live_bitmap_ = NULL; } Object* Heap::AllocObject(Class* klass, size_t num_bytes) { @@ -100,8 +125,8 @@ void Heap::RecordFree(Space* space, const Object* obj) { } Object* Heap::Allocate(size_t size) { - CHECK_EQ(spaces_.size(), 1u); - Space* space = spaces_[0]; + DCHECK(alloc_space_ != NULL); + Space* space = alloc_space_; Object* obj = Allocate(space, size); if (obj != NULL) { RecordAllocation(space, obj); diff --git a/src/heap.h b/src/heap.h index 247c6bad6d..99f655424a 100644 --- a/src/heap.h +++ b/src/heap.h @@ -24,7 +24,9 @@ class Heap { typedef void (RootVistor)(Object* root, void* arg); - static bool Init(size_t starting_size, size_t maximum_size); + // Create a heap with the requested sizes. optional boot image may + // be NULL, otherwise it is an image filename created by ImageWriter. + static bool Init(size_t starting_size, size_t maximum_size, const char* boot_image_file_name); static void Destroy(); @@ -53,10 +55,6 @@ class Heap { return mark_bitmap_; } - static size_t GetMaximumSize() { - return maximum_size_; - } - private: // Allocates uninitialized storage. static Object* Allocate(size_t num_bytes); @@ -73,13 +71,13 @@ class Heap { static std::vector<Space*> spaces_; + // default Space for allocations + static Space* alloc_space_; + static HeapBitmap* mark_bitmap_; static HeapBitmap* live_bitmap_; - // The startup size of the heap in bytes. - static size_t startup_size_; - // The maximum size of the heap in bytes. static size_t maximum_size_; diff --git a/src/image.cc b/src/image.cc new file mode 100644 index 0000000000..a41bf5ba14 --- /dev/null +++ b/src/image.cc @@ -0,0 +1,10 @@ +// Copyright 2011 Google Inc. All Rights Reserved. + +#include "image.h" + +namespace art { + +const byte ImageHeader::kImageMagic[] = { 'i', 'm', 'g', '\n' }; +const byte ImageHeader::kImageVersion[] = { '0', '0', '1', '\0' }; + +} // namespace art diff --git a/src/image.h b/src/image.h new file mode 100644 index 0000000000..c4123e8c6c --- /dev/null +++ b/src/image.h @@ -0,0 +1,47 @@ +// Copyright 2011 Google Inc. All Rights Reserved. + +#ifndef ART_SRC_IMAGE_H_ +#define ART_SRC_IMAGE_H_ + +#include <string.h> + +#include "globals.h" + +namespace art { + +// header of image files written by ImageWriter, read and validated by Space. +class ImageHeader { + public: + ImageHeader() {} + + ImageHeader(uint32_t base_addr) : base_addr_(base_addr) { + memcpy(magic_, kImageMagic, sizeof(kImageMagic)); + memcpy(version_, kImageVersion, sizeof(kImageVersion)); + } + + bool IsValid() { + if (memcmp(magic_, kImageMagic, sizeof(kImageMagic) != 0)) { + return false; + } + if (memcmp(version_, kImageVersion, sizeof(kImageVersion) != 0)) { + return false; + } + return true; + } + + byte* GetBaseAddr() const { + return reinterpret_cast<byte*>(base_addr_); + } + + private: + static const byte kImageMagic[4]; + static const byte kImageVersion[4]; + + byte magic_[4]; + byte version_[4]; + uint32_t base_addr_; +}; + +} // namespace art + +#endif // ART_SRC_IMAGE_H_ diff --git a/src/image_test.cc b/src/image_test.cc index c3662cd403..6cfa62d1d1 100644 --- a/src/image_test.cc +++ b/src/image_test.cc @@ -1,28 +1,42 @@ // Copyright 2011 Google Inc. All Rights Reserved. #include "common_test.h" +#include "file.h" +#include "image.h" #include "image_writer.h" +#include "os.h" +#include "space.h" #include "gtest/gtest.h" namespace art { -class ImageTest : public RuntimeTest {}; +class ImageTest : public CommonTest {}; TEST_F(ImageTest, WriteRead) { scoped_ptr<DexFile> libcore_dex_file(GetLibCoreDex()); EXPECT_TRUE(libcore_dex_file.get() != NULL); - // TODO garbage collect before writing + // TODO: garbage collect before writing const std::vector<Space*>& spaces = Heap::GetSpaces(); // can't currently deal with writing a space that might have pointers between spaces - CHECK_EQ(1U, spaces.size()); + ASSERT_EQ(1U, spaces.size()); + Space* space = spaces[0]; ImageWriter writer; ScratchFile tmp; const int image_base = 0x5000000; - bool success = writer.Write(spaces[0], tmp.GetFilename(), reinterpret_cast<byte*>(image_base)); - EXPECT_TRUE(success); + bool success = writer.Write(space, tmp.GetFilename(), reinterpret_cast<byte*>(image_base)); + ASSERT_TRUE(success); + + { + scoped_ptr<File> file(OS::OpenFile(tmp.GetFilename(), false)); + ASSERT_TRUE(file != NULL); + ImageHeader image_header; + file->ReadFully(&image_header, sizeof(image_header)); + ASSERT_TRUE(image_header.IsValid()); + ASSERT_GE(sizeof(image_header) + space->Size(), static_cast<size_t>(file->Length())); + } // tear down old runtime and make a new one delete runtime_.release(); diff --git a/src/image_writer.cc b/src/image_writer.cc index beb736662b..b6625d099c 100644 --- a/src/image_writer.cc +++ b/src/image_writer.cc @@ -8,6 +8,7 @@ #include "file.h" #include "globals.h" #include "heap.h" +#include "image.h" #include "logging.h" #include "object.h" #include "space.h" @@ -33,11 +34,9 @@ bool ImageWriter::Write(Space* space, const char* filename, byte* image_base) { bool ImageWriter::Init(Space* space) { size_t size = space->Size(); int prot = PROT_READ | PROT_WRITE; - int flags = MAP_PRIVATE | MAP_ANONYMOUS; size_t length = RoundUp(size, kPageSize); - image_.reset(MemMap::Map(length, prot, flags)); + image_.reset(MemMap::Map(length, prot)); if (image_ == NULL) { - PLOG(ERROR) << "mmap failed"; return false; } return true; @@ -56,8 +55,9 @@ void ImageWriter::CalculateNewObjectOffsets() { HeapBitmap* heap_bitmap = Heap::GetLiveBits(); DCHECK(heap_bitmap != NULL); DCHECK_EQ(0U, image_top_); - // leave a header, ensures objects have non-zero offset for DCHECKs - image_top_ += 8; // 64-bit-alignment + ImageHeader image_header(reinterpret_cast<uint32_t>(image_base_)); + memcpy(image_->GetAddress(), &image_header, sizeof(image_header)); + image_top_ += RoundUp(sizeof(image_header), 8); // 64-bit-alignment heap_bitmap->Walk(CalculateNewObjectOffsetsCallback, this); DCHECK_LT(image_top_, image_->GetLength()); // Note that top_ is left at end of used space @@ -88,7 +88,7 @@ void ImageWriter::FixupObject(Object* orig, Object* copy) { DCHECK(orig != NULL); DCHECK(copy != NULL); copy->klass_ = down_cast<Class*>(GetImageAddress(orig->klass_)); - // TODO specical case init of pointers to malloc data (or removal of these pointers) + // TODO: specical case init of pointers to malloc data (or removal of these pointers) if (orig->IsObjectArray()) { FixupObjectArray(orig->AsObjectArray<Object>(), down_cast<ObjectArray<Object>*>(copy)); } else { diff --git a/src/image_writer.h b/src/image_writer.h index 0af45c662b..003c4e950f 100644 --- a/src/image_writer.h +++ b/src/image_writer.h @@ -1,7 +1,7 @@ // Copyright 2011 Google Inc. All Rights Reserved. -#ifndef ART_SRC_IMAGE_H_ -#define ART_SRC_IMAGE_H_ +#ifndef ART_SRC_IMAGE_WRITER_H_ +#define ART_SRC_IMAGE_WRITER_H_ #include <cstddef> #include <stdint.h> @@ -66,4 +66,4 @@ class ImageWriter { } // namespace art -#endif // ART_SRC_IMAGE_H_ +#endif // ART_SRC_IMAGE_WRITER_H_ diff --git a/src/indirect_reference_table.cc b/src/indirect_reference_table.cc index 20f39b6d5d..b30ce539aa 100644 --- a/src/indirect_reference_table.cc +++ b/src/indirect_reference_table.cc @@ -269,7 +269,7 @@ bool IndirectReferenceTable::Remove(uint32_t cookie, IndirectRef iref) { segmentState.parts.topIndex = topIndex; } else { segmentState.parts.topIndex = topIndex-1; - LOG(INFO) << "+++ ate last entry " << topIndex-1; + //LOG(INFO) << "+++ ate last entry " << topIndex-1; } } else { /* diff --git a/src/indirect_reference_table_test.cc b/src/indirect_reference_table_test.cc index 97e3a70259..352ebd5c2e 100644 --- a/src/indirect_reference_table_test.cc +++ b/src/indirect_reference_table_test.cc @@ -22,7 +22,7 @@ namespace art { -class IndirectReferenceTableTest : public RuntimeTest { +class IndirectReferenceTableTest : public CommonTest { }; TEST_F(IndirectReferenceTableTest, BasicTest) { diff --git a/src/intern_table_test.cc b/src/intern_table_test.cc index daae8410fb..e6bb611312 100644 --- a/src/intern_table_test.cc +++ b/src/intern_table_test.cc @@ -9,7 +9,7 @@ namespace art { -class InternTableTest : public RuntimeTest {}; +class InternTableTest : public CommonTest {}; TEST_F(InternTableTest, Intern) { InternTable intern_table; diff --git a/src/jni_compiler.cc b/src/jni_compiler.cc index f3d13c8cc7..440dda54b9 100644 --- a/src/jni_compiler.cc +++ b/src/jni_compiler.cc @@ -382,9 +382,7 @@ void* JniCompiler::AllocateCode(size_t size) { JniCompiler::JniCompiler() { // TODO: this shouldn't be managed by the JniCompiler, we should have a // code cache. - jni_code_.reset(MemMap::Map(kPageSize, - PROT_READ | PROT_WRITE | PROT_EXEC, - MAP_ANONYMOUS | MAP_PRIVATE)); + jni_code_.reset(MemMap::Map(kPageSize, PROT_READ | PROT_WRITE | PROT_EXEC)); CHECK(jni_code_ != NULL); jni_code_top_ = jni_code_->GetAddress(); } diff --git a/src/jni_compiler_test.cc b/src/jni_compiler_test.cc index 59b9004667..854df887d0 100644 --- a/src/jni_compiler_test.cc +++ b/src/jni_compiler_test.cc @@ -15,14 +15,12 @@ namespace art { -class JniCompilerTest : public RuntimeTest { +class JniCompilerTest : public CommonTest { protected: virtual void SetUp() { - RuntimeTest::SetUp(); + CommonTest::SetUp(); // Create thunk code that performs the native to managed transition - thunk_code_.reset(MemMap::Map(kPageSize, - PROT_READ | PROT_WRITE | PROT_EXEC, - MAP_ANONYMOUS | MAP_PRIVATE)); + thunk_code_.reset(MemMap::Map(kPageSize, PROT_READ | PROT_WRITE | PROT_EXEC)); CHECK(thunk_code_ != NULL); Assembler thk_asm; // TODO: shouldn't have machine specific code in a general purpose file diff --git a/src/jni_internal.cc b/src/jni_internal.cc index ef3bf57770..599b0fe68f 100644 --- a/src/jni_internal.cc +++ b/src/jni_internal.cc @@ -3,9 +3,10 @@ #include "jni_internal.h" #include <cstdarg> -#include <vector> -#include <utility> +#include <dlfcn.h> #include <sys/mman.h> +#include <utility> +#include <vector> #include "class_linker.h" #include "jni.h" @@ -18,6 +19,235 @@ namespace art { +enum JNI_OnLoadState { + kPending = 0, /* initial state, must be zero */ + kFailed, + kOkay, +}; + +struct SharedLibrary { + SharedLibrary() : jni_on_load_lock("JNI_OnLoad") { + } + + // Path to library "/system/lib/libjni.so". + std::string path; + + // The void* returned by dlopen(3). + void* handle; + + // The ClassLoader this library is associated with. + Object* class_loader; + + // Guards remaining items. + Mutex jni_on_load_lock; + // Wait for JNI_OnLoad in other thread. + pthread_cond_t jni_on_load_cond; + // Recursive invocation guard. + uint32_t jni_on_load_tid; + // Result of earlier JNI_OnLoad call. + JNI_OnLoadState jni_on_load_result; +}; + +/* + * Check the result of an earlier call to JNI_OnLoad on this library. If + * the call has not yet finished in another thread, wait for it. + */ +bool CheckOnLoadResult(JavaVMExt* vm, SharedLibrary* library) { + Thread* self = Thread::Current(); + if (library->jni_on_load_tid == self->GetId()) { + // Check this so we don't end up waiting for ourselves. We need + // to return "true" so the caller can continue. + LOG(INFO) << *self << " recursive attempt to load library " + << "\"" << library->path << "\""; + return true; + } + + UNIMPLEMENTED(ERROR) << "need to pthread_cond_wait!"; + // MutexLock mu(&library->jni_on_load_lock); + while (library->jni_on_load_result == kPending) { + if (vm->verbose_jni) { + LOG(INFO) << "[" << *self << " waiting for \"" << library->path << "\" " + << "JNI_OnLoad...]"; + } + Thread::State old_state = self->GetState(); + self->SetState(Thread::kWaiting); // TODO: VMWAIT + // pthread_cond_wait(&library->jni_on_load_cond, &library->jni_on_load_lock); + self->SetState(old_state); + } + + bool okay = (library->jni_on_load_result == kOkay); + if (vm->verbose_jni) { + LOG(INFO) << "[Earlier JNI_OnLoad for \"" << library->path << "\" " + << (okay ? "succeeded" : "failed") << "]"; + } + return okay; +} + +typedef int (*JNI_OnLoadFn)(JavaVM*, void*); + +/* + * Load native code from the specified absolute pathname. Per the spec, + * if we've already loaded a library with the specified pathname, we + * return without doing anything. + * + * TODO? for better results we should absolutify the pathname. For fully + * correct results we should stat to get the inode and compare that. The + * existing implementation is fine so long as everybody is using + * System.loadLibrary. + * + * The library will be associated with the specified class loader. The JNI + * spec says we can't load the same library into more than one class loader. + * + * Returns "true" on success. On failure, sets *detail to a + * human-readable description of the error or NULL if no detail is + * available; ownership of the string is transferred to the caller. + */ +bool JavaVMExt::LoadNativeLibrary(const std::string& path, Object* class_loader, char** detail) { + *detail = NULL; + + // See if we've already loaded this library. If we have, and the class loader + // matches, return successfully without doing anything. + SharedLibrary* library = libraries[path]; + if (library != NULL) { + if (library->class_loader != class_loader) { + LOG(WARNING) << "Shared library \"" << path << "\" already opened by " + << "ClassLoader " << library->class_loader << "; " + << "can't open in " << class_loader; + *detail = strdup("already opened by different ClassLoader"); + return false; + } + if (verbose_jni) { + LOG(INFO) << "[Shared library \"" << path << "\" already loaded in " + << "ClassLoader " << class_loader << "]"; + } + if (!CheckOnLoadResult(this, library)) { + *detail = strdup("JNI_OnLoad failed before"); + return false; + } + return true; + } + + // Open the shared library. Because we're using a full path, the system + // doesn't have to search through LD_LIBRARY_PATH. (It may do so to + // resolve this library's dependencies though.) + + // Failures here are expected when java.library.path has several entries + // and we have to hunt for the lib. + + // The current version of the dynamic linker prints detailed information + // about dlopen() failures. Some things to check if the message is + // cryptic: + // - make sure the library exists on the device + // - verify that the right path is being opened (the debug log message + // above can help with that) + // - check to see if the library is valid (e.g. not zero bytes long) + // - check config/prelink-linux-arm.map to ensure that the library + // is listed and is not being overrun by the previous entry (if + // loading suddenly stops working on a prelinked library, this is + // a good one to check) + // - write a trivial app that calls sleep() then dlopen(), attach + // to it with "strace -p <pid>" while it sleeps, and watch for + // attempts to open nonexistent dependent shared libs + + // TODO: automate some of these checks! + + // This can execute slowly for a large library on a busy system, so we + // want to switch from RUNNING to VMWAIT while it executes. This allows + // the GC to ignore us. + Thread* self = Thread::Current(); + Thread::State old_state = self->GetState(); + self->SetState(Thread::kWaiting); // TODO: VMWAIT + void* handle = dlopen(path.c_str(), RTLD_LAZY); + self->SetState(old_state); + + if (verbose_jni) { + LOG(INFO) << "[Call to dlopen(\"" << path << "\") returned " << handle << "]"; + } + + if (handle == NULL) { + *detail = strdup(dlerror()); + return false; + } + + // Create a new entry. + library = new SharedLibrary; + library->path = path; + library->handle = handle; + library->class_loader = class_loader; + UNIMPLEMENTED(ERROR) << "missing pthread_cond_init"; + // pthread_cond_init(&library->onLoadCond, NULL); + library->jni_on_load_tid = self->GetId(); + + libraries[path] = library; + +// if (pNewEntry != pActualEntry) { +// LOG(INFO) << "WOW: we lost a race to add a shared library (\"" << path << "\" ClassLoader=" << class_loader <<")"; +// freeSharedLibEntry(pNewEntry); +// return CheckOnLoadResult(this, pActualEntry); +// } else + { + if (verbose_jni) { + LOG(INFO) << "[Added shared library \"" << path << "\" for ClassLoader " << class_loader << "]"; + } + + bool result = true; + void* sym = dlsym(handle, "JNI_OnLoad"); + if (sym == NULL) { + if (verbose_jni) { + LOG(INFO) << "[No JNI_OnLoad found in \"" << path << "\"]"; + } + } else { + // Call JNI_OnLoad. We have to override the current class + // loader, which will always be "null" since the stuff at the + // top of the stack is around Runtime.loadLibrary(). (See + // the comments in the JNI FindClass function.) + UNIMPLEMENTED(WARNING) << "need to override current class loader"; + JNI_OnLoadFn jni_on_load = reinterpret_cast<JNI_OnLoadFn>(sym); + //Object* prevOverride = self->classLoaderOverride; + //self->classLoaderOverride = classLoader; + + old_state = self->GetState(); + self->SetState(Thread::kNative); + if (verbose_jni) { + LOG(INFO) << "[Calling JNI_OnLoad in \"" << path << "\"]"; + } + int version = (*jni_on_load)(reinterpret_cast<JavaVM*>(this), NULL); + self->SetState(old_state); + + UNIMPLEMENTED(WARNING) << "need to restore current class loader"; + //self->classLoaderOverride = prevOverride; + + if (version != JNI_VERSION_1_2 && + version != JNI_VERSION_1_4 && + version != JNI_VERSION_1_6) { + LOG(WARNING) << "JNI_OnLoad in \"" << path << "\" returned " + << "bad version: " << version; + // It's unwise to call dlclose() here, but we can mark it + // as bad and ensure that future load attempts will fail. + // We don't know how far JNI_OnLoad got, so there could + // be some partially-initialized stuff accessible through + // newly-registered native method calls. We could try to + // unregister them, but that doesn't seem worthwhile. + result = false; + } else { + if (verbose_jni) { + LOG(INFO) << "[Returned " << (result ? "successfully" : "failure") + << " from JNI_OnLoad in \"" << path << "\"]"; + } + } + } + + library->jni_on_load_result = result ? kOkay : kFailed; + library->jni_on_load_tid = 0; + + // Broadcast a wakeup to anybody sleeping on the condition variable. + UNIMPLEMENTED(ERROR) << "missing pthread_cond_broadcast"; + // MutexLock mu(&library->jni_on_load_lock); + // pthread_cond_broadcast(&library->jni_on_load_cond); + return result; + } +} + // Entry/exit processing for all JNI calls. // // This performs the necessary thread state switching, lets us amortize the @@ -25,7 +255,8 @@ namespace art { // that are using a JNIEnv on the wrong thread. class ScopedJniThreadState { public: - explicit ScopedJniThreadState(JNIEnv* env) { + explicit ScopedJniThreadState(JNIEnv* env) + : env_(reinterpret_cast<JNIEnvExt*>(env)) { self_ = ThreadForEnv(env); self_->SetState(Thread::kRunnable); } @@ -34,6 +265,10 @@ class ScopedJniThreadState { self_->SetState(Thread::kNative); } + JNIEnvExt* Env() { + return env_; + } + Thread* Self() { return self_; } @@ -52,14 +287,114 @@ class ScopedJniThreadState { return self; } + JNIEnvExt* env_; Thread* self_; DISALLOW_COPY_AND_ASSIGN(ScopedJniThreadState); }; +/* + * Add a local reference for an object to the current stack frame. When + * the native function returns, the reference will be discarded. + * + * We need to allow the same reference to be added multiple times. + * + * This will be called on otherwise unreferenced objects. We cannot do + * GC allocations here, and it's best if we don't grab a mutex. + * + * Returns the local reference (currently just the same pointer that was + * passed in), or NULL on failure. + */ template<typename T> T AddLocalReference(ScopedJniThreadState& ts, Object* obj) { - UNIMPLEMENTED(WARNING); - return reinterpret_cast<T>(obj); + if (obj == NULL) { + return NULL; + } + + IndirectReferenceTable& locals = ts.Env()->locals; + + uint32_t cookie = IRT_FIRST_SEGMENT; // TODO + IndirectRef ref = locals.Add(cookie, obj); + if (ref == NULL) { + // TODO: just change Add's DCHECK to CHECK and lose this? + locals.Dump(); + LOG(FATAL) << "Failed adding to JNI local reference table " + << "(has " << locals.Capacity() << " entries)"; + // TODO: dvmDumpThread(dvmThreadSelf(), false); + } + +#if 0 // TODO: fix this to understand PushLocalFrame, so we can turn it on. + if (ts.Env()->check_jni) { + size_t entry_count = locals.Capacity(); + if (entry_count > 16) { + std::string class_name(PrettyDescriptor(obj->GetClass()->GetDescriptor())); + LOG(WARNING) << "Warning: more than 16 JNI local references: " + << entry_count << " (most recent was a " << class_name << ")"; + locals.Dump(); + // TODO: dvmDumpThread(dvmThreadSelf(), false); + // dvmAbort(); + } + } +#endif + + if (false /*gDvmJni.workAroundAppJniBugs*/) { // TODO + // Hand out direct pointers to support broken old apps. + return reinterpret_cast<T>(obj); + } + + return reinterpret_cast<T>(ref); +} + +template<typename T> +T Decode(ScopedJniThreadState& ts, jobject obj) { + if (obj == NULL) { + return NULL; + } + + IndirectRef ref = reinterpret_cast<IndirectRef>(obj); + IndirectRefKind kind = GetIndirectRefKind(ref); + Object* result; + switch (kind) { + case kLocal: + { + IndirectReferenceTable& locals = ts.Env()->locals; + result = locals.Get(ref); + break; + } + case kGlobal: + { + JavaVMExt* vm = Runtime::Current()->GetJavaVM(); + IndirectReferenceTable& globals = vm->globals; + MutexLock mu(&vm->globals_lock); + result = globals.Get(ref); + break; + } + case kWeakGlobal: + { + JavaVMExt* vm = Runtime::Current()->GetJavaVM(); + IndirectReferenceTable& weak_globals = vm->weak_globals; + MutexLock mu(&vm->weak_globals_lock); + result = weak_globals.Get(ref); + if (result == kClearedJniWeakGlobal) { + // This is a special case where it's okay to return NULL. + return NULL; + } + break; + } + case kInvalid: + default: + if (false /*gDvmJni.workAroundAppJniBugs*/) { // TODO + // Assume an invalid local reference is actually a direct pointer. + return reinterpret_cast<T>(obj); + } + LOG(FATAL) << "Invalid indirect reference " << obj; + return reinterpret_cast<T>(kInvalidIndirectRefObject); + } + + if (result == NULL) { + LOG(FATAL) << "JNI ERROR (app bug): use of deleted " << kind << ": " + << obj; + } + return reinterpret_cast<T>(result); } void CreateInvokeStub(Assembler* assembler, Method* method); @@ -85,7 +420,7 @@ bool EnsureInvokeStub(Method* method) { return true; } -byte* CreateArgArray(Method* method, va_list ap) { +byte* CreateArgArray(ScopedJniThreadState& ts, Method* method, va_list ap) { size_t num_bytes = method->NumArgArrayBytes(); scoped_array<byte> arg_array(new byte[num_bytes]); const StringPiece& shorty = method->GetShorty(); @@ -104,8 +439,7 @@ byte* CreateArgArray(Method* method, va_list ap) { offset += 4; break; case 'L': { - // TODO: DecodeReference - Object* obj = reinterpret_cast<Object*>(va_arg(ap, jobject)); + Object* obj = Decode<Object*>(ts, va_arg(ap, jobject)); *reinterpret_cast<Object**>(&arg_array[offset]) = obj; offset += sizeof(Object*); break; @@ -123,7 +457,7 @@ byte* CreateArgArray(Method* method, va_list ap) { return arg_array.release(); } -byte* CreateArgArray(Method* method, jvalue* args) { +byte* CreateArgArray(ScopedJniThreadState& ts, Method* method, jvalue* args) { size_t num_bytes = method->NumArgArrayBytes(); scoped_array<byte> arg_array(new byte[num_bytes]); const StringPiece& shorty = method->GetShorty(); @@ -142,8 +476,7 @@ byte* CreateArgArray(Method* method, jvalue* args) { offset += 4; break; case 'L': { - // TODO: DecodeReference - Object* obj = reinterpret_cast<Object*>(args[i - 1].l); + Object* obj = Decode<Object*>(ts, args[i - 1].l); *reinterpret_cast<Object**>(&arg_array[offset]) = obj; offset += sizeof(Object*); break; @@ -161,8 +494,8 @@ byte* CreateArgArray(Method* method, jvalue* args) { return arg_array.release(); } -JValue InvokeWithArgArray(Thread* self, Object* obj, jmethodID method_id, - byte* args) { +JValue InvokeWithArgArray(ScopedJniThreadState& ts, + Object* obj, jmethodID method_id, byte* args) { // TODO: DecodeReference Method* method = reinterpret_cast<Method*>(method_id); // Call the invoke stub associated with the method @@ -170,22 +503,22 @@ JValue InvokeWithArgArray(Thread* self, Object* obj, jmethodID method_id, const Method::InvokeStub* stub = method->GetInvokeStub(); CHECK(stub != NULL); JValue result; - (*stub)(method, obj, self, args, &result); + (*stub)(method, obj, ts.Self(), args, &result); return result; } -JValue InvokeWithJValues(Thread* self, Object* obj, jmethodID method_id, - jvalue* args) { +JValue InvokeWithJValues(ScopedJniThreadState& ts, + Object* obj, jmethodID method_id, jvalue* args) { Method* method = reinterpret_cast<Method*>(method_id); - scoped_array<byte> arg_array(CreateArgArray(method, args)); - return InvokeWithArgArray(self, obj, method_id, arg_array.get()); + scoped_array<byte> arg_array(CreateArgArray(ts, method, args)); + return InvokeWithArgArray(ts, obj, method_id, arg_array.get()); } -JValue InvokeWithVarArgs(Thread* self, Object* obj, jmethodID method_id, - va_list args) { +JValue InvokeWithVarArgs(ScopedJniThreadState& ts, + Object* obj, jmethodID method_id, va_list args) { Method* method = reinterpret_cast<Method*>(method_id); - scoped_array<byte> arg_array(CreateArgArray(method, args)); - return InvokeWithArgArray(self, obj, method_id, arg_array.get()); + scoped_array<byte> arg_array(CreateArgArray(ts, method, args)); + return InvokeWithArgArray(ts, obj, method_id, arg_array.get()); } jint GetVersion(JNIEnv* env) { @@ -320,14 +653,14 @@ void FatalError(JNIEnv* env, const char* msg) { jint PushLocalFrame(JNIEnv* env, jint cap) { ScopedJniThreadState ts(env); - UNIMPLEMENTED(FATAL); - return 0; + UNIMPLEMENTED(WARNING) << "ignoring PushLocalFrame(" << cap << ")"; + return JNI_OK; } jobject PopLocalFrame(JNIEnv* env, jobject res) { ScopedJniThreadState ts(env); - UNIMPLEMENTED(FATAL); - return NULL; + UNIMPLEMENTED(WARNING) << "ignoring PopLocalFrame " << res; + return res; } jobject NewGlobalRef(JNIEnv* env, jobject lobj) { @@ -343,7 +676,23 @@ void DeleteGlobalRef(JNIEnv* env, jobject gref) { void DeleteLocalRef(JNIEnv* env, jobject obj) { ScopedJniThreadState ts(env); - UNIMPLEMENTED(WARNING); + + if (obj == NULL) { + return; + } + + IndirectReferenceTable& locals = ts.Env()->locals; + + uint32_t cookie = IRT_FIRST_SEGMENT; // TODO + if (!locals.Remove(cookie, obj)) { + // Attempting to delete a local reference that is not in the + // topmost local reference frame is a no-op. DeleteLocalRef returns + // void and doesn't throw any exceptions, but we should probably + // complain about it so the user will notice that things aren't + // going quite the way they expect. + LOG(WARNING) << "JNI WARNING: DeleteLocalRef(" << obj << ") " + << "failed to find entry"; + } } jboolean IsSameObject(JNIEnv* env, jobject obj1, jobject obj2) { @@ -403,10 +752,8 @@ jboolean IsInstanceOf(JNIEnv* env, jobject jobj, jclass clazz) { // NB. JNI is different from regular Java instanceof in this respect return JNI_TRUE; } else { - // TODO: retrieve handle value for object - Object* obj = reinterpret_cast<Object*>(jobj); - // TODO: retrieve handle value for class - Class* klass = reinterpret_cast<Class*>(clazz); + Object* obj = Decode<Object*>(ts, jobj); + Class* klass = Decode<Class*>(ts, clazz); return Object::InstanceOf(obj, klass) ? JNI_TRUE : JNI_FALSE; } } @@ -414,8 +761,7 @@ jboolean IsInstanceOf(JNIEnv* env, jobject jobj, jclass clazz) { jmethodID GetMethodID(JNIEnv* env, jclass clazz, const char* name, const char* sig) { ScopedJniThreadState ts(env); - // TODO: retrieve handle value for class - Class* klass = reinterpret_cast<Class*>(clazz); + Class* klass = Decode<Class*>(ts, clazz); if (!klass->IsInitialized()) { // TODO: initialize the class } @@ -965,8 +1311,7 @@ void SetDoubleField(JNIEnv* env, jobject obj, jfieldID fieldID, jdouble val) { jmethodID GetStaticMethodID(JNIEnv* env, jclass clazz, const char* name, const char* sig) { ScopedJniThreadState ts(env); - // TODO: DecodeReference - Class* klass = reinterpret_cast<Class*>(clazz); + Class* klass = Decode<Class*>(ts, clazz); if (!klass->IsInitialized()) { // TODO: initialize the class } @@ -1005,21 +1350,21 @@ jobject CallStaticObjectMethod(JNIEnv* env, ScopedJniThreadState ts(env); va_list ap; va_start(ap, methodID); - JValue result = InvokeWithVarArgs(ts.Self(), NULL, methodID, ap); + JValue result = InvokeWithVarArgs(ts, NULL, methodID, ap); return AddLocalReference<jobject>(ts, result.l); } jobject CallStaticObjectMethodV(JNIEnv* env, jclass clazz, jmethodID methodID, va_list args) { ScopedJniThreadState ts(env); - JValue result = InvokeWithVarArgs(ts.Self(), NULL, methodID, args); + JValue result = InvokeWithVarArgs(ts, NULL, methodID, args); return AddLocalReference<jobject>(ts, result.l); } jobject CallStaticObjectMethodA(JNIEnv* env, jclass clazz, jmethodID methodID, jvalue* args) { ScopedJniThreadState ts(env); - JValue result = InvokeWithJValues(ts.Self(), NULL, methodID, args); + JValue result = InvokeWithJValues(ts, NULL, methodID, args); return AddLocalReference<jobject>(ts, result.l); } @@ -1028,171 +1373,171 @@ jboolean CallStaticBooleanMethod(JNIEnv* env, ScopedJniThreadState ts(env); va_list ap; va_start(ap, methodID); - return InvokeWithVarArgs(ts.Self(), NULL, methodID, ap).z; + return InvokeWithVarArgs(ts, NULL, methodID, ap).z; } jboolean CallStaticBooleanMethodV(JNIEnv* env, jclass clazz, jmethodID methodID, va_list args) { ScopedJniThreadState ts(env); - return InvokeWithVarArgs(ts.Self(), NULL, methodID, args).z; + return InvokeWithVarArgs(ts, NULL, methodID, args).z; } jboolean CallStaticBooleanMethodA(JNIEnv* env, jclass clazz, jmethodID methodID, jvalue* args) { ScopedJniThreadState ts(env); - return InvokeWithJValues(ts.Self(), NULL, methodID, args).z; + return InvokeWithJValues(ts, NULL, methodID, args).z; } jbyte CallStaticByteMethod(JNIEnv* env, jclass clazz, jmethodID methodID, ...) { ScopedJniThreadState ts(env); va_list ap; va_start(ap, methodID); - return InvokeWithVarArgs(ts.Self(), NULL, methodID, ap).b; + return InvokeWithVarArgs(ts, NULL, methodID, ap).b; } jbyte CallStaticByteMethodV(JNIEnv* env, jclass clazz, jmethodID methodID, va_list args) { ScopedJniThreadState ts(env); - return InvokeWithVarArgs(ts.Self(), NULL, methodID, args).b; + return InvokeWithVarArgs(ts, NULL, methodID, args).b; } jbyte CallStaticByteMethodA(JNIEnv* env, jclass clazz, jmethodID methodID, jvalue* args) { ScopedJniThreadState ts(env); - return InvokeWithJValues(ts.Self(), NULL, methodID, args).b; + return InvokeWithJValues(ts, NULL, methodID, args).b; } jchar CallStaticCharMethod(JNIEnv* env, jclass clazz, jmethodID methodID, ...) { ScopedJniThreadState ts(env); va_list ap; va_start(ap, methodID); - return InvokeWithVarArgs(ts.Self(), NULL, methodID, ap).c; + return InvokeWithVarArgs(ts, NULL, methodID, ap).c; } jchar CallStaticCharMethodV(JNIEnv* env, jclass clazz, jmethodID methodID, va_list args) { ScopedJniThreadState ts(env); - return InvokeWithVarArgs(ts.Self(), NULL, methodID, args).c; + return InvokeWithVarArgs(ts, NULL, methodID, args).c; } jchar CallStaticCharMethodA(JNIEnv* env, jclass clazz, jmethodID methodID, jvalue* args) { ScopedJniThreadState ts(env); - return InvokeWithJValues(ts.Self(), NULL, methodID, args).c; + return InvokeWithJValues(ts, NULL, methodID, args).c; } jshort CallStaticShortMethod(JNIEnv* env, jclass clazz, jmethodID methodID, ...) { ScopedJniThreadState ts(env); va_list ap; va_start(ap, methodID); - return InvokeWithVarArgs(ts.Self(), NULL, methodID, ap).s; + return InvokeWithVarArgs(ts, NULL, methodID, ap).s; } jshort CallStaticShortMethodV(JNIEnv* env, jclass clazz, jmethodID methodID, va_list args) { ScopedJniThreadState ts(env); - return InvokeWithVarArgs(ts.Self(), NULL, methodID, args).s; + return InvokeWithVarArgs(ts, NULL, methodID, args).s; } jshort CallStaticShortMethodA(JNIEnv* env, jclass clazz, jmethodID methodID, jvalue* args) { ScopedJniThreadState ts(env); - return InvokeWithJValues(ts.Self(), NULL, methodID, args).s; + return InvokeWithJValues(ts, NULL, methodID, args).s; } jint CallStaticIntMethod(JNIEnv* env, jclass clazz, jmethodID methodID, ...) { ScopedJniThreadState ts(env); va_list ap; va_start(ap, methodID); - return InvokeWithVarArgs(ts.Self(), NULL, methodID, ap).i; + return InvokeWithVarArgs(ts, NULL, methodID, ap).i; } jint CallStaticIntMethodV(JNIEnv* env, jclass clazz, jmethodID methodID, va_list args) { ScopedJniThreadState ts(env); - return InvokeWithVarArgs(ts.Self(), NULL, methodID, args).i; + return InvokeWithVarArgs(ts, NULL, methodID, args).i; } jint CallStaticIntMethodA(JNIEnv* env, jclass clazz, jmethodID methodID, jvalue* args) { ScopedJniThreadState ts(env); - return InvokeWithJValues(ts.Self(), NULL, methodID, args).i; + return InvokeWithJValues(ts, NULL, methodID, args).i; } jlong CallStaticLongMethod(JNIEnv* env, jclass clazz, jmethodID methodID, ...) { ScopedJniThreadState ts(env); va_list ap; va_start(ap, methodID); - return InvokeWithVarArgs(ts.Self(), NULL, methodID, ap).j; + return InvokeWithVarArgs(ts, NULL, methodID, ap).j; } jlong CallStaticLongMethodV(JNIEnv* env, jclass clazz, jmethodID methodID, va_list args) { ScopedJniThreadState ts(env); - return InvokeWithVarArgs(ts.Self(), NULL, methodID, args).j; + return InvokeWithVarArgs(ts, NULL, methodID, args).j; } jlong CallStaticLongMethodA(JNIEnv* env, jclass clazz, jmethodID methodID, jvalue* args) { ScopedJniThreadState ts(env); - return InvokeWithJValues(ts.Self(), NULL, methodID, args).j; + return InvokeWithJValues(ts, NULL, methodID, args).j; } jfloat CallStaticFloatMethod(JNIEnv* env, jclass cls, jmethodID methodID, ...) { ScopedJniThreadState ts(env); va_list ap; va_start(ap, methodID); - return InvokeWithVarArgs(ts.Self(), NULL, methodID, ap).f; + return InvokeWithVarArgs(ts, NULL, methodID, ap).f; } jfloat CallStaticFloatMethodV(JNIEnv* env, jclass clazz, jmethodID methodID, va_list args) { ScopedJniThreadState ts(env); - return InvokeWithVarArgs(ts.Self(), NULL, methodID, args).f; + return InvokeWithVarArgs(ts, NULL, methodID, args).f; } jfloat CallStaticFloatMethodA(JNIEnv* env, jclass clazz, jmethodID methodID, jvalue* args) { ScopedJniThreadState ts(env); - return InvokeWithJValues(ts.Self(), NULL, methodID, args).f; + return InvokeWithJValues(ts, NULL, methodID, args).f; } jdouble CallStaticDoubleMethod(JNIEnv* env, jclass cls, jmethodID methodID, ...) { ScopedJniThreadState ts(env); va_list ap; va_start(ap, methodID); - return InvokeWithVarArgs(ts.Self(), NULL, methodID, ap).d; + return InvokeWithVarArgs(ts, NULL, methodID, ap).d; } jdouble CallStaticDoubleMethodV(JNIEnv* env, jclass clazz, jmethodID methodID, va_list args) { ScopedJniThreadState ts(env); - return InvokeWithVarArgs(ts.Self(), NULL, methodID, args).d; + return InvokeWithVarArgs(ts, NULL, methodID, args).d; } jdouble CallStaticDoubleMethodA(JNIEnv* env, jclass clazz, jmethodID methodID, jvalue* args) { ScopedJniThreadState ts(env); - return InvokeWithJValues(ts.Self(), NULL, methodID, args).d; + return InvokeWithJValues(ts, NULL, methodID, args).d; } void CallStaticVoidMethod(JNIEnv* env, jclass cls, jmethodID methodID, ...) { ScopedJniThreadState ts(env); va_list ap; va_start(ap, methodID); - InvokeWithVarArgs(ts.Self(), NULL, methodID, ap); + InvokeWithVarArgs(ts, NULL, methodID, ap); } void CallStaticVoidMethodV(JNIEnv* env, jclass cls, jmethodID methodID, va_list args) { ScopedJniThreadState ts(env); - InvokeWithVarArgs(ts.Self(), NULL, methodID, args); + InvokeWithVarArgs(ts, NULL, methodID, args); } void CallStaticVoidMethodA(JNIEnv* env, jclass cls, jmethodID methodID, jvalue* args) { ScopedJniThreadState ts(env); - InvokeWithJValues(ts.Self(), NULL, methodID, args); + InvokeWithJValues(ts, NULL, methodID, args); } jfieldID GetStaticFieldID(JNIEnv* env, @@ -1375,9 +1720,8 @@ jobject GetObjectArrayElement(JNIEnv* env, jobjectArray array, jsize index) { void SetObjectArrayElement(JNIEnv* env, jobjectArray java_array, jsize index, jobject java_value) { ScopedJniThreadState ts(env); - // TODO: DecodeReference - ObjectArray<Object>* array = reinterpret_cast<ObjectArray<Object>*>(java_array); - Object* value = reinterpret_cast<Object*>(java_value); + ObjectArray<Object>* array = Decode<ObjectArray<Object>*>(ts, java_array); + Object* value = Decode<Object*>(ts, java_value); array->Set(index, value); } @@ -1428,8 +1772,7 @@ jobjectArray NewObjectArray(JNIEnv* env, jsize length, jclass element_jclass, jo CHECK_GE(length, 0); // TODO: ReportJniError // Compute the array class corresponding to the given element class. - // TODO: DecodeReference - Class* element_class = reinterpret_cast<Class*>(element_jclass); + Class* element_class = Decode<Class*>(ts, element_jclass); std::string descriptor; descriptor += "["; descriptor += element_class->GetDescriptor().ToString(); @@ -1651,11 +1994,16 @@ void SetDoubleArrayRegion(JNIEnv* env, jint RegisterNatives(JNIEnv* env, jclass clazz, const JNINativeMethod* methods, jint nMethods) { ScopedJniThreadState ts(env); - // TODO: retrieve handle value for class - Class* klass = reinterpret_cast<Class*>(clazz); + Class* klass = Decode<Class*>(ts, clazz); for(int i = 0; i < nMethods; i++) { const char* name = methods[i].name; const char* sig = methods[i].signature; + + if (*sig == '!') { + // TODO: fast jni. it's too noisy to log all these. + ++sig; + } + Method* method = klass->FindDirectMethod(name, sig); if (method == NULL) { method = klass->FindVirtualMethod(name, sig); @@ -1704,7 +2052,7 @@ jint GetJavaVM(JNIEnv* env, JavaVM** vm) { ScopedJniThreadState ts(env); Runtime* runtime = Runtime::Current(); if (runtime != NULL) { - *vm = runtime->GetJavaVM(); + *vm = reinterpret_cast<JavaVM*>(runtime->GetJavaVM()); } else { *vm = NULL; } @@ -2058,7 +2406,7 @@ extern "C" jint JNI_CreateJavaVM(JavaVM** p_vm, void** p_env, void* vm_args) { return JNI_ERR; } else { *p_env = reinterpret_cast<JNIEnv*>(Thread::Current()->GetJniEnv()); - *p_vm = runtime->GetJavaVM(); + *p_vm = reinterpret_cast<JavaVM*>(runtime->GetJavaVM()); return JNI_OK; } } @@ -2069,7 +2417,7 @@ extern "C" jint JNI_GetCreatedJavaVMs(JavaVM** vms, jsize, jsize* vm_count) { *vm_count = 0; } else { *vm_count = 1; - vms[0] = runtime->GetJavaVM(); + vms[0] = reinterpret_cast<JavaVM*>(runtime->GetJavaVM()); } return JNI_OK; } @@ -2085,7 +2433,6 @@ jint DestroyJavaVM(JavaVM* vm) { } else { JavaVMExt* raw_vm = reinterpret_cast<JavaVMExt*>(vm); delete raw_vm->runtime; - raw_vm->runtime = NULL; return JNI_OK; } } @@ -2177,12 +2524,15 @@ static const size_t kGlobalsMax = 51200; // Arbitrary sanity check. static const size_t kWeakGlobalsInitial = 16; // Arbitrary. static const size_t kWeakGlobalsMax = 51200; // Arbitrary sanity check. -JavaVMExt::JavaVMExt(Runtime* runtime, bool check_jni) +JavaVMExt::JavaVMExt(Runtime* runtime, bool check_jni, bool verbose_jni) : fns(&gInvokeInterface), runtime(runtime), check_jni(check_jni), + verbose_jni(verbose_jni), pin_table("pin table", kPinTableInitialSize, kPinTableMaxSize), + globals_lock("JNI global reference table"), globals(kGlobalsInitial, kGlobalsMax, kGlobal), + weak_globals_lock("JNI weak global reference table"), weak_globals(kWeakGlobalsInitial, kWeakGlobalsMax, kWeakGlobal) { } diff --git a/src/jni_internal.h b/src/jni_internal.h index 50e3925618..74366d3eea 100644 --- a/src/jni_internal.h +++ b/src/jni_internal.h @@ -9,14 +9,37 @@ #include "indirect_reference_table.h" #include "macros.h" #include "reference_table.h" +#include "thread.h" + +#include <map> +#include <string> namespace art { class Runtime; -class Thread; +class SharedLibrary; struct JavaVMExt { - JavaVMExt(Runtime* runtime, bool check_jni); + JavaVMExt(Runtime* runtime, bool check_jni, bool verbose_jni); + + /* + * Load native code from the specified absolute pathname. Per the spec, + * if we've already loaded a library with the specified pathname, we + * return without doing anything. + * + * TODO: for better results we should canonicalize the pathname. For fully + * correct results we should stat to get the inode and compare that. The + * existing implementation is fine so long as everybody is using + * System.loadLibrary. + * + * The library will be associated with the specified class loader. The JNI + * spec says we can't load the same library into more than one class loader. + * + * Returns true on success. On failure, returns false and sets *detail to a + * human-readable description of the error or NULL if no detail is + * available; ownership of the string is transferred to the caller. + */ + bool LoadNativeLibrary(const std::string& path, Object* class_loader, char** detail); // Must be first to correspond with JNIEnv. const struct JNIInvokeInterface* fns; @@ -24,15 +47,20 @@ struct JavaVMExt { Runtime* runtime; bool check_jni; + bool verbose_jni; // Used to hold references to pinned primitive arrays. ReferenceTable pin_table; // JNI global references. + Mutex globals_lock; IndirectReferenceTable globals; // JNI weak global references. + Mutex weak_globals_lock; IndirectReferenceTable weak_globals; + + std::map<std::string, SharedLibrary*> libraries; }; struct JNIEnvExt { diff --git a/src/jni_internal_test.cc b/src/jni_internal_test.cc index 04b3225f4d..4fa60c53da 100644 --- a/src/jni_internal_test.cc +++ b/src/jni_internal_test.cc @@ -10,10 +10,10 @@ namespace art { -class JniInternalTest : public RuntimeTest { +class JniInternalTest : public CommonTest { protected: virtual void SetUp() { - RuntimeTest::SetUp(); + CommonTest::SetUp(); env_ = Thread::Current()->GetJniEnv(); } JNIEnv* env_; diff --git a/src/mark_stack.cc b/src/mark_stack.cc index 19dc1c1aeb..663cdf72c3 100644 --- a/src/mark_stack.cc +++ b/src/mark_stack.cc @@ -10,9 +10,9 @@ namespace art { -MarkStack* MarkStack::Create(size_t maximum_size) { +MarkStack* MarkStack::Create() { scoped_ptr<MarkStack> mark_stack(new MarkStack()); - bool success = mark_stack->Init(maximum_size); + bool success = mark_stack->Init(); if (!success) { return NULL; } else { @@ -20,11 +20,10 @@ MarkStack* MarkStack::Create(size_t maximum_size) { } } -bool MarkStack::Init(size_t maximum_size) { +bool MarkStack::Init() { size_t length = 64 * MB; - mem_map_.reset(MemMap::Map(length, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS)); + mem_map_.reset(MemMap::Map(length, PROT_READ | PROT_WRITE)); if (mem_map_ == NULL) { - PLOG(ERROR) << "mmap failed"; return false; } byte* addr = mem_map_->GetAddress(); diff --git a/src/mark_stack.h b/src/mark_stack.h index 38252c2750..79026cc1fe 100644 --- a/src/mark_stack.h +++ b/src/mark_stack.h @@ -14,7 +14,7 @@ class Object; class MarkStack { public: - static MarkStack* Create(size_t maximum_size); + static MarkStack* Create(); ~MarkStack(); @@ -41,7 +41,7 @@ class MarkStack { base_(NULL), limit_(NULL), ptr_(NULL) { } - bool Init(size_t maximum_size); + bool Init(); // Memory mapping of the mark stack. scoped_ptr<MemMap> mem_map_; diff --git a/src/mark_sweep.cc b/src/mark_sweep.cc index 06a1c92055..8541f76c8b 100644 --- a/src/mark_sweep.cc +++ b/src/mark_sweep.cc @@ -22,7 +22,7 @@ size_t MarkSweep::reference_pendingNext_offset_ = 0; // TODO size_t MarkSweep::finalizer_reference_zombie_offset_ = 0; // TODO bool MarkSweep::Init() { - mark_stack_ = MarkStack::Create(Heap::GetMaximumSize()); + mark_stack_ = MarkStack::Create(); if (mark_stack_ == NULL) { return false; } diff --git a/src/mem_map.h b/src/mem_map.h index 9357b9b7e5..f44939cac7 100644 --- a/src/mem_map.h +++ b/src/mem_map.h @@ -30,18 +30,28 @@ class MemMap { // Request an anonymous region of a specified length. // // On success, returns returns a MemMap instance. On failure, returns a NULL; - static MemMap* Map(size_t length, int prot, int flags) { + static MemMap* Map(size_t length, int prot) { + return Map(NULL, length, prot); + } + + // Request an anonymous region of a specified length and a requested base address. + // + // On success, returns returns a MemMap instance. On failure, returns a NULL; + static MemMap* Map(byte* addr, size_t length, int prot) { + CHECK_NE(0U, length); + CHECK_NE(0, prot); size_t page_aligned_size = RoundUp(length, kPageSize); - byte* addr = reinterpret_cast<byte*>(mmap(NULL, - page_aligned_size, - prot, - MAP_ANONYMOUS | flags, - -1, - 0)); - if (addr == MAP_FAILED) { + byte* actual = reinterpret_cast<byte*>(mmap(addr, + page_aligned_size, + prot, + MAP_PRIVATE | MAP_ANONYMOUS, + -1, + 0)); + if (actual == MAP_FAILED) { + PLOG(ERROR) << "mmap failed"; return NULL; } - return new MemMap(addr, length, addr, page_aligned_size); + return new MemMap(actual, length, actual, page_aligned_size); } // Map part of a file, taking care of non-page aligned offsets. The @@ -49,20 +59,33 @@ class MemMap { // // On success, returns returns a MemMap instance. On failure, returns a NULL; static MemMap* Map(size_t length, int prot, int flags, int fd, off_t start) { + return Map(NULL, length, prot, flags, fd, start); + } + + // Map part of a file, taking care of non-page aligned offsets. The + // "start" offset is absolute, not relative. This version allows + // requesting a specific address for the base of the mapping. + // + // On success, returns returns a MemMap instance. On failure, returns a NULL; + static MemMap* Map(byte* addr, size_t length, int prot, int flags, int fd, off_t start) { + CHECK_NE(0U, length); + CHECK_NE(0, prot); + CHECK(flags & MAP_SHARED || flags & MAP_PRIVATE); // adjust to be page-aligned int page_offset = start % kPageSize; off_t page_aligned_offset = start - page_offset; - size_t page_aligned_size = length + page_offset; - byte* addr = reinterpret_cast<byte*>(mmap(NULL, - page_aligned_size, - prot, - MAP_FILE | flags, - fd, - page_aligned_offset)); - if (addr == MAP_FAILED) { + size_t page_aligned_size = RoundUp(length + page_offset, kPageSize); + byte* actual = reinterpret_cast<byte*>(mmap(addr, + page_aligned_size, + prot, + flags, + fd, + page_aligned_offset)); + if (actual == MAP_FAILED) { + PLOG(ERROR) << "mmap failed"; return NULL; } - return new MemMap(addr+page_offset, length, addr, page_aligned_size); + return new MemMap(actual + page_offset, length, actual, page_aligned_size); } ~MemMap() { @@ -91,6 +114,10 @@ class MemMap { return length_; } + byte* GetLimit() const { + return addr_ + length_; + } + private: MemMap(byte* addr, size_t length, void* base_addr, size_t base_length) : addr_(addr), length_(length), base_addr_(base_addr), base_length_(base_length) { diff --git a/src/object.h b/src/object.h index da4d6f33df..1789ec828e 100644 --- a/src/object.h +++ b/src/object.h @@ -712,7 +712,7 @@ class ClassLoader : public Object { Object* packages_; ClassLoader* parent_; - // TODO remove once we can create a real PathClassLoader + // TODO: remove once we can create a real PathClassLoader std::vector<const DexFile*> class_path_; DISALLOW_IMPLICIT_CONSTRUCTORS(ClassLoader); @@ -1293,7 +1293,7 @@ class String : public Object { int32_t hash_code) { String* string = Alloc(GetJavaLangString(), utf16_length); - // TODO use 16-bit wide memset variant + // TODO: use 16-bit wide memset variant for (int i = 0; i < utf16_length; i++ ) { string->array_->Set(i, utf16_data_in[i]); } @@ -1435,7 +1435,7 @@ class String : public Object { } bool Equals(const String* that) const { - // TODO short circuit on hash_code_ + // TODO: short circuit on hash_code_ if (this->GetLength() != that->GetLength()) { return false; } diff --git a/src/object_bitmap.cc b/src/object_bitmap.cc index fc3c2ee4ee..7de498860d 100644 --- a/src/object_bitmap.cc +++ b/src/object_bitmap.cc @@ -36,9 +36,8 @@ HeapBitmap* HeapBitmap::Create(byte* base, size_t length) { bool HeapBitmap::Init(const byte* base, size_t max_size) { CHECK(base != NULL); size_t length = HB_OFFSET_TO_INDEX(max_size) * kWordSize; - mem_map_.reset(MemMap::Map(length, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS)); + mem_map_.reset(MemMap::Map(length, PROT_READ | PROT_WRITE)); if (mem_map_ == NULL) { - LOG(ERROR) << "mmap failed"; return false; } words_ = reinterpret_cast<word*>(mem_map_->GetAddress()); diff --git a/src/object_test.cc b/src/object_test.cc index e11cf075f3..8f9bed6850 100644 --- a/src/object_test.cc +++ b/src/object_test.cc @@ -13,7 +13,7 @@ namespace art { -class ObjectTest : public RuntimeTest { +class ObjectTest : public CommonTest { protected: void AssertString(size_t length, const char* utf8_in, diff --git a/src/os_linux.cc b/src/os_linux.cc index 8f5f56a7ea..6d84b51a04 100644 --- a/src/os_linux.cc +++ b/src/os_linux.cc @@ -30,7 +30,7 @@ File* OS::FileFromFd(const char* name, int fd) { bool OS::FileExists(const char* name) { struct stat st; if (stat(name, &st) == 0) { - return S_ISREG(st.st_mode); // TODO Deal with symlinks? + return S_ISREG(st.st_mode); // TODO: Deal with symlinks? } else { return false; } diff --git a/src/reference_table_test.cc b/src/reference_table_test.cc index c61e3c99c3..4728cb03e4 100644 --- a/src/reference_table_test.cc +++ b/src/reference_table_test.cc @@ -8,7 +8,7 @@ namespace art { -class ReferenceTableTest : public RuntimeTest { +class ReferenceTableTest : public CommonTest { }; TEST_F(ReferenceTableTest, Basics) { diff --git a/src/runtime.cc b/src/runtime.cc index 2476837830..335396d2ae 100644 --- a/src/runtime.cc +++ b/src/runtime.cc @@ -9,6 +9,7 @@ #include "class_linker.h" #include "heap.h" +#include "jni_internal.h" #include "scoped_ptr.h" #include "thread.h" @@ -17,12 +18,13 @@ namespace art { Runtime* Runtime::instance_ = NULL; Runtime::~Runtime() { - // TODO: use a smart pointer instead. + // TODO: use smart pointers instead. (we'll need the pimpl idiom.) delete class_linker_; Heap::Destroy(); delete thread_list_; + delete java_vm_; // TODO: acquire a static mutex on Runtime to avoid racing. - CHECK(instance_ == this); + CHECK(instance_ == NULL || instance_ == this); instance_ = NULL; } @@ -88,7 +90,6 @@ void ParseClassPath(const char* class_path, std::vector<std::string>& vec) { // [gG] gigabytes. // // "s" should point just past the "-Xm?" part of the string. -// "min" specifies the lowest acceptable value described by "s". // "div" specifies a divisor, e.g. 1024 if the value must be a multiple // of 1024. // @@ -111,35 +112,35 @@ size_t ParseMemoryOption(const char *s, size_t div) { // there should be exactly one more character // that specifies a multiplier. if (*s2 != '\0') { - // The remainder of the string is either a single multiplier - // character, or nothing to indicate that the value is in - // bytes. - char c = *s2++; - if (*s2 == '\0') { - size_t mul; - if (c == '\0') { - mul = 1; - } else if (c == 'k' || c == 'K') { - mul = 1024; - } else if (c == 'm' || c == 'M') { - mul = 1024 * 1024; - } else if (c == 'g' || c == 'G') { - mul = 1024 * 1024 * 1024; - } else { - // Unknown multiplier character. - return 0; - } - - if (val <= std::numeric_limits<size_t>::max() / mul) { - val *= mul; - } else { - // Clamp to a multiple of 1024. - val = std::numeric_limits<size_t>::max() & ~(1024-1); - } + // The remainder of the string is either a single multiplier + // character, or nothing to indicate that the value is in + // bytes. + char c = *s2++; + if (*s2 == '\0') { + size_t mul; + if (c == '\0') { + mul = 1; + } else if (c == 'k' || c == 'K') { + mul = KB; + } else if (c == 'm' || c == 'M') { + mul = MB; + } else if (c == 'g' || c == 'G') { + mul = GB; } else { - // There's more than one character after the numeric part. + // Unknown multiplier character. return 0; } + + if (val <= std::numeric_limits<size_t>::max() / mul) { + val *= mul; + } else { + // Clamp to a multiple of 1024. + val = std::numeric_limits<size_t>::max() & ~(1024-1); + } + } else { + // There's more than one character after the numeric part. + return 0; + } } // The man page says that a -Xm value must be a multiple of 1024. if (val % div == 0) { @@ -150,6 +151,16 @@ size_t ParseMemoryOption(const char *s, size_t div) { return 0; } +void LoadJniLibrary(JavaVMExt* vm, const char* name) { + // TODO: OS_SHARED_LIB_FORMAT_STR + std::string mapped_name(StringPrintf("lib%s.so", name)); + char* reason = NULL; + if (!vm->LoadNativeLibrary(mapped_name, NULL, &reason)) { + LOG(FATAL) << "LoadNativeLibrary failed for \"" << mapped_name << "\": " + << reason; + } +} + DexFile* Open(const std::string& filename) { if (filename.size() < 4) { LOG(WARNING) << "Ignoring short classpath entry '" << filename << "'"; @@ -181,14 +192,17 @@ Runtime::ParsedOptions* Runtime::ParsedOptions::Create(const Options& options, b const char* boot_class_path = getenv("BOOTCLASSPATH"); parsed->boot_image_ = NULL; #ifdef NDEBUG - // CheckJNI is off by default for regular builds... + // -Xcheck:jni and -verbose:jni are off by default for regular builds... parsed->check_jni_ = false; #else // ...but on by default in debug builds. parsed->check_jni_ = true; + parsed->verbose_.insert("jni"); #endif parsed->heap_initial_size_ = Heap::kInitialSize; parsed->heap_maximum_size_ = Heap::kMaximumSize; + parsed->stack_size_ = Thread::kDefaultStackSize; + parsed->hook_vfprintf_ = vfprintf; parsed->hook_exit_ = exit; parsed->hook_abort_ = abort; @@ -198,19 +212,62 @@ Runtime::ParsedOptions* Runtime::ParsedOptions::Create(const Options& options, b if (option.starts_with("-Xbootclasspath:")) { boot_class_path = option.substr(strlen("-Xbootclasspath:")).data(); } else if (option == "bootclasspath") { - parsed->boot_class_path_ = *reinterpret_cast<const std::vector<DexFile*>*>(options[i].second); + const void* dex_vector = options[i].second; + const std::vector<DexFile*>* v = reinterpret_cast<const std::vector<DexFile*>*>(dex_vector); + if (v == NULL) { + if (ignore_unrecognized) { + continue; + } + // TODO: usage + LOG(FATAL) << "Could not parse " << option; + return NULL; + } + parsed->boot_class_path_ = *v; } else if (option.starts_with("-Xbootimage:")) { parsed->boot_image_ = option.substr(strlen("-Xbootimage:")).data(); } else if (option.starts_with("-Xcheck:jni")) { parsed->check_jni_ = true; } else if (option.starts_with("-Xms")) { - parsed->heap_initial_size_ = ParseMemoryOption(option.substr(strlen("-Xms")).data(), 1024); + size_t size = ParseMemoryOption(option.substr(strlen("-Xms")).data(), 1024); + if (size == 0) { + if (ignore_unrecognized) { + continue; + } + // TODO: usage + LOG(FATAL) << "Could not parse " << option; + return NULL; + } + parsed->heap_initial_size_ = size; } else if (option.starts_with("-Xmx")) { - parsed->heap_maximum_size_ = ParseMemoryOption(option.substr(strlen("-Xmx")).data(), 1024); + size_t size = ParseMemoryOption(option.substr(strlen("-Xmx")).data(), 1024); + if (size == 0) { + if (ignore_unrecognized) { + continue; + } + // TODO: usage + LOG(FATAL) << "Could not parse " << option; + return NULL; + } + parsed->heap_maximum_size_ = size; + } else if (option.starts_with("-Xss")) { + size_t size = ParseMemoryOption(option.substr(strlen("-Xss")).data(), 1); + if (size == 0) { + if (ignore_unrecognized) { + continue; + } + // TODO: usage + LOG(FATAL) << "Could not parse " << option; + return NULL; + } + parsed->stack_size_ = size; } else if (option.starts_with("-D")) { parsed->properties_.push_back(option.substr(strlen("-D")).data()); } else if (option.starts_with("-verbose:")) { - Split(option.substr(strlen("-verbose:")).data(), ",", parsed->verbose_); + std::vector<std::string> verbose_options; + Split(option.substr(strlen("-verbose:")).data(), ",", verbose_options); + for (size_t i = 0; i < verbose_options.size(); ++i) { + parsed->verbose_.insert(verbose_options[i]); + } } else if (option == "vfprintf") { parsed->hook_vfprintf_ = reinterpret_cast<int (*)(FILE *, const char*, va_list)>(options[i].second); } else if (option == "exit") { @@ -250,34 +307,46 @@ Runtime* Runtime::Create(const Options& options, bool ignore_unrecognized) { bool success = runtime->Init(options, ignore_unrecognized); if (!success) { return NULL; - } else { - return Runtime::instance_ = runtime.release(); } + instance_ = runtime.release(); + + // Most JNI libraries can just use System.loadLibrary, but you can't + // if you're the library that implements System.loadLibrary! + LoadJniLibrary(instance_->GetJavaVM(), "javacore"); + + return instance_; } -bool Runtime::Init(const Options& options, bool ignore_unrecognized) { +bool Runtime::Init(const Options& raw_options, bool ignore_unrecognized) { CHECK_EQ(kPageSize, sysconf(_SC_PAGE_SIZE)); - scoped_ptr<ParsedOptions> parsed_options(ParsedOptions::Create(options, ignore_unrecognized)); - if (parsed_options == NULL) { + scoped_ptr<ParsedOptions> options(ParsedOptions::Create(raw_options, ignore_unrecognized)); + if (options == NULL) { return false; } - vfprintf_ = parsed_options->hook_vfprintf_; - exit_ = parsed_options->hook_exit_; - abort_ = parsed_options->hook_abort_; + vfprintf_ = options->hook_vfprintf_; + exit_ = options->hook_exit_; + abort_ = options->hook_abort_; + stack_size_ = options->stack_size_; thread_list_ = ThreadList::Create(); - Heap::Init(parsed_options->heap_initial_size_, - parsed_options->heap_maximum_size_); + if (!Heap::Init(options->heap_initial_size_, + options->heap_maximum_size_, + options->boot_image_)) { + return false; + } - java_vm_.reset(reinterpret_cast<JavaVM*>(new JavaVMExt(this, parsed_options->check_jni_))); + bool verbose_jni = options->verbose_.find("jni") != options->verbose_.end(); + java_vm_ = new JavaVMExt(this, options->check_jni_, verbose_jni); - Thread::Init(); + if (!Thread::Init()) { + return false; + } Thread* current_thread = Thread::Attach(this); thread_list_->Register(current_thread); - class_linker_ = ClassLinker::Create(parsed_options->boot_class_path_); + class_linker_ = ClassLinker::Create(options->boot_class_path_); return true; } diff --git a/src/runtime.h b/src/runtime.h index 828e677feb..9879023512 100644 --- a/src/runtime.h +++ b/src/runtime.h @@ -3,21 +3,26 @@ #ifndef ART_SRC_RUNTIME_H_ #define ART_SRC_RUNTIME_H_ +#include <stdio.h> + #include <string> #include <utility> #include <vector> -#include "jni.h" +#include <jni.h> + #include "globals.h" #include "macros.h" #include "scoped_ptr.h" #include "stringpiece.h" +#include "unordered_set.h" namespace art { class ClassLinker; class DexFile; class Heap; +class JavaVMExt; class String; class ThreadList; @@ -36,10 +41,11 @@ class Runtime { bool check_jni_; size_t heap_initial_size_; size_t heap_maximum_size_; + size_t stack_size_; jint (*hook_vfprintf_)(FILE* stream, const char* format, va_list ap); void (*hook_exit_)(jint status); void (*hook_abort_)(); - std::vector<std::string> verbose_; + std::tr1::unordered_set<std::string> verbose_; std::vector<std::string> properties_; private: @@ -73,27 +79,34 @@ class Runtime { ~Runtime(); - ClassLinker* GetClassLinker() { + size_t GetStackSize() const { + return stack_size_; + } + + ClassLinker* GetClassLinker() const { return class_linker_; } - JavaVM* GetJavaVM() const { - return java_vm_.get(); + JavaVMExt* GetJavaVM() const { + return java_vm_; } private: static void PlatformAbort(const char*, int); - Runtime() : thread_list_(NULL), class_linker_(NULL) {} + Runtime() : stack_size_(0), thread_list_(NULL), class_linker_(NULL) {} // Initializes a new uninitialized runtime. bool Init(const Options& options, bool ignore_unrecognized); + // The default stack size for managed threads created by the runtime. + size_t stack_size_; + ThreadList* thread_list_; ClassLinker* class_linker_; - scoped_ptr<JavaVM> java_vm_; + JavaVMExt* java_vm_; // Hooks supported by JNI_CreateJavaVM jint (*vfprintf_)(FILE* stream, const char* format, va_list ap); diff --git a/src/runtime_test.cc b/src/runtime_test.cc index 3aa257d74c..6fc14b8872 100644 --- a/src/runtime_test.cc +++ b/src/runtime_test.cc @@ -1,16 +1,14 @@ // Copyright 2011 Google Inc. All Rights Reserved. #include "runtime.h" - -#include "gtest/gtest.h" +#include "common_test.h" namespace art { void ParseClassPath(const char* class_path, std::vector<std::string>& vec); -} -namespace { +class RuntimeTest : public CommonTest {}; -TEST(RuntimeTest, ParseClassPath) { +TEST_F(RuntimeTest, ParseClassPath) { std::vector<std::string> vec; art::ParseClassPath("", vec); @@ -66,4 +64,48 @@ TEST(RuntimeTest, ParseClassPath) { vec.clear(); } -} // namespace +TEST_F(RuntimeTest, ParsedOptions) { + void* test_vfprintf = reinterpret_cast<void*>(0xa); + void* test_abort = reinterpret_cast<void*>(0xb); + void* test_exit = reinterpret_cast<void*>(0xc); + void* null = reinterpret_cast<void*>(NULL); + scoped_ptr<const DexFile> java_lang_dex_file(GetLibCoreDex()); + std::vector<const DexFile*> boot_class_path; + boot_class_path.push_back(java_lang_dex_file_.get()); + + Runtime::Options options; + options.push_back(std::make_pair("-Xbootclasspath:class_path_foo:class_path_bar", null)); + options.push_back(std::make_pair("bootclasspath", &boot_class_path)); + options.push_back(std::make_pair("-Xbootimage:boot_image", null)); + options.push_back(std::make_pair("-Xcheck:jni", null)); + options.push_back(std::make_pair("-Xms2048", null)); + options.push_back(std::make_pair("-Xmx4k", null)); + options.push_back(std::make_pair("-Xss1m", null)); + options.push_back(std::make_pair("-Dfoo=bar", null)); + options.push_back(std::make_pair("-Dbaz=qux", null)); + options.push_back(std::make_pair("-verbose:gc,class,jni", null)); + options.push_back(std::make_pair("vfprintf", test_vfprintf)); + options.push_back(std::make_pair("abort", test_abort)); + options.push_back(std::make_pair("exit", test_exit)); + scoped_ptr<Runtime::ParsedOptions> parsed(Runtime::ParsedOptions::Create(options, false)); + ASSERT_TRUE(parsed != NULL); + + EXPECT_EQ(1U, parsed->boot_class_path_.size()); // bootclasspath overrides -Xbootclasspath + EXPECT_STREQ("boot_image", parsed->boot_image_); + EXPECT_EQ(true, parsed->check_jni_); + EXPECT_EQ(2048U, parsed->heap_initial_size_); + EXPECT_EQ(4 * KB, parsed->heap_maximum_size_); + EXPECT_EQ(1 * MB, parsed->stack_size_); + EXPECT_TRUE(test_vfprintf == parsed->hook_vfprintf_); + EXPECT_TRUE(test_exit == parsed->hook_exit_); + EXPECT_TRUE(test_abort == parsed->hook_abort_); + ASSERT_EQ(3U, parsed->verbose_.size()); + EXPECT_TRUE(parsed->verbose_.find("gc") != parsed->verbose_.end()); + EXPECT_TRUE(parsed->verbose_.find("class") != parsed->verbose_.end()); + EXPECT_TRUE(parsed->verbose_.find("jni") != parsed->verbose_.end()); + ASSERT_EQ(2U, parsed->properties_.size()); + EXPECT_EQ("foo=bar", parsed->properties_[0]); + EXPECT_EQ("baz=qux", parsed->properties_[1]); +} + +} // namespace art diff --git a/src/space.cc b/src/space.cc index c3d763c4fe..8048f29198 100644 --- a/src/space.cc +++ b/src/space.cc @@ -4,16 +4,19 @@ #include <sys/mman.h> +#include "file.h" +#include "image.h" #include "logging.h" #include "mspace.h" +#include "os.h" #include "scoped_ptr.h" #include "utils.h" namespace art { -Space* Space::Create(size_t startup_size, size_t maximum_size) { - scoped_ptr<Space> space(new Space(startup_size, maximum_size)); - bool success = space->Init(); +Space* Space::Create(size_t initial_size, size_t maximum_size, byte* requested_base) { + scoped_ptr<Space> space(new Space()); + bool success = space->Init(initial_size, maximum_size, requested_base); if (!success) { return NULL; } else { @@ -21,18 +24,31 @@ Space* Space::Create(size_t startup_size, size_t maximum_size) { } } +Space* Space::Create(const char* image_file_name) { + CHECK(image_file_name != NULL); + scoped_ptr<Space> space(new Space()); + bool success = space->Init(image_file_name); + if (!success) { + return NULL; + } else { + return space.release(); + } +} + +Space::~Space() {} + void* Space::CreateMallocSpace(void* base, - size_t startup_size, + size_t initial_size, size_t maximum_size) { errno = 0; bool is_locked = false; - size_t commit_size = startup_size / 2; + size_t commit_size = initial_size / 2; void* msp = create_contiguous_mspace_with_base(commit_size, maximum_size, is_locked, base); if (msp != NULL) { // Do not permit the heap grow past the starting size without our // intervention. - mspace_set_max_allowed_footprint(msp, startup_size); + mspace_set_max_allowed_footprint(msp, initial_size); } else { // There is no guarantee that errno has meaning when the call // fails, but it often does. @@ -41,35 +57,60 @@ void* Space::CreateMallocSpace(void* base, return msp; } -bool Space::Init() { - if (!(startup_size_ <= maximum_size_)) { +bool Space::Init(size_t initial_size, size_t maximum_size, byte* requested_base) { + if (!(initial_size <= maximum_size)) { return false; } - size_t length = RoundUp(maximum_size_, kPageSize); + size_t length = RoundUp(maximum_size, kPageSize); int prot = PROT_READ | PROT_WRITE; - int flags = MAP_PRIVATE | MAP_ANONYMOUS; - mem_map_.reset(MemMap::Map(length, prot, flags)); - if (mem_map_ == NULL) { - PLOG(ERROR) << "mmap failed"; + scoped_ptr<MemMap> mem_map(MemMap::Map(requested_base, length, prot)); + if (mem_map == NULL) { return false; } + Init(mem_map.release()); + maximum_size_ = maximum_size; + mspace_ = CreateMallocSpace(base_, initial_size, maximum_size); + return (mspace_ != NULL); +} + +void Space::Init(MemMap* mem_map) { + mem_map_.reset(mem_map); base_ = mem_map_->GetAddress(); - limit_ = base_ + length; - mspace_ = CreateMallocSpace(base_, startup_size_, maximum_size_); - if (mspace_ == NULL) { - mem_map_->Unmap(); + limit_ = base_ + mem_map->GetLength(); +} + + +bool Space::Init(const char* image_file_name) { + scoped_ptr<File> file(OS::OpenFile(image_file_name, false)); + if (file == NULL) { + return false; + } + ImageHeader image_header; + bool success = file->ReadFully(&image_header, sizeof(image_header)); + if (!success || !image_header.IsValid()) { + return false; + } + scoped_ptr<MemMap> map(MemMap::Map(image_header.GetBaseAddr(), + file->Length(), + PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_FIXED, + file->Fd(), + 0)); + if (map == NULL) { return false; } + CHECK_EQ(image_header.GetBaseAddr(), map->GetAddress()); + Init(map.release()); return true; } -Space::~Space() {} - Object* Space::AllocWithoutGrowth(size_t num_bytes) { + DCHECK(mspace_ != NULL); return reinterpret_cast<Object*>(mspace_calloc(mspace_, 1, num_bytes)); } Object* Space::AllocWithGrowth(size_t num_bytes) { + DCHECK(mspace_ != NULL); // Grow as much as possible within the mspace. size_t max_allowed = maximum_size_; mspace_set_max_allowed_footprint(mspace_, max_allowed); @@ -83,6 +124,7 @@ Object* Space::AllocWithGrowth(size_t num_bytes) { } size_t Space::Free(void* ptr) { + DCHECK(mspace_ != NULL); DCHECK(ptr != NULL); size_t num_bytes = mspace_usable_size(mspace_, ptr); mspace_free(mspace_, ptr); @@ -90,6 +132,7 @@ size_t Space::Free(void* ptr) { } size_t Space::AllocationSize(const Object* obj) { + DCHECK(mspace_ != NULL); return mspace_usable_size(mspace_, obj) + kChunkOverhead; } @@ -116,6 +159,7 @@ void Space::Trim() { } size_t Space::MaxAllowedFootprint() { + DCHECK(mspace_ != NULL); return mspace_max_allowed_footprint(mspace_); } diff --git a/src/space.h b/src/space.h index 7b72bba53c..57f676def3 100644 --- a/src/space.h +++ b/src/space.h @@ -15,7 +15,11 @@ class Object; // A space contains memory allocated for managed objects. class Space { public: - static Space* Create(size_t startup_size, size_t maximum_size); + // create a Space with the requested sizes requesting a specific base address. + static Space* Create(size_t initial_size, size_t maximum_size, byte* requested_base); + + // create a Space from an image file. cannot be used for future allocation or collected. + static Space* Create(const char* image); ~Space(); @@ -53,22 +57,27 @@ class Space { // The boundary tag overhead. static const size_t kChunkOverhead = kWordSize; - Space(size_t startup_size, size_t maximum_size) : - mspace_(NULL), - base_(NULL), - startup_size_(startup_size), - maximum_size_(maximum_size) { - } + // create a Space from an existing memory mapping, taking ownership of the address space. + static Space* Create(MemMap* mem_map); + + Space() : mspace_(NULL), maximum_size_(0), base_(0), limit_(0) {} // Initializes the space and underlying storage. - bool Init(); + bool Init(size_t initial_size, size_t maximum_size, byte* requested_base); + + // Initializes the space from existing storage, taking ownership of the storage. + void Init(MemMap* map); + + // Initializes the space from an image file + bool Init(const char* image_file_name); - void* CreateMallocSpace(void* base, size_t startup_size, - size_t maximum_size); + void* CreateMallocSpace(void* base, size_t initial_size, size_t maximum_size); static void DontNeed(void* start, void* end, void* num_bytes); + // TODO: have a Space subclass for methods that depend on mspace_ and maximum_size_ void* mspace_; + size_t maximum_size_; scoped_ptr<MemMap> mem_map_; @@ -76,13 +85,9 @@ class Space { byte* limit_; - size_t startup_size_; - - size_t maximum_size_; - - bool is_condemned_; + // bool is_condemned_; // TODO: with IsCondemned - DISALLOW_IMPLICIT_CONSTRUCTORS(Space); + DISALLOW_COPY_AND_ASSIGN(Space); }; } // namespace art diff --git a/src/space_test.cc b/src/space_test.cc index f3c713bb99..73840214b1 100644 --- a/src/space_test.cc +++ b/src/space_test.cc @@ -12,23 +12,23 @@ namespace art { TEST(SpaceTest, Init) { { // Less than - scoped_ptr<Space> space(Space::Create(16 * MB, 32 * MB)); + scoped_ptr<Space> space(Space::Create(16 * MB, 32 * MB, NULL)); EXPECT_TRUE(space != NULL); } { // Equal to - scoped_ptr<Space> space(Space::Create(16 * MB, 16 * MB)); + scoped_ptr<Space> space(Space::Create(16 * MB, 16 * MB, NULL)); EXPECT_TRUE(space != NULL); } { // Greater than - scoped_ptr<Space> space(Space::Create(32 * MB, 16 * MB)); + scoped_ptr<Space> space(Space::Create(32 * MB, 16 * MB, NULL)); EXPECT_TRUE(space == NULL); } } TEST(SpaceTest, AllocAndFree) { - scoped_ptr<Space> space(Space::Create(4 * MB, 16 * MB)); + scoped_ptr<Space> space(Space::Create(4 * MB, 16 * MB, NULL)); ASSERT_TRUE(space != NULL); // Succeeds, fits without adjusting the max allowed footprint. diff --git a/src/thread.cc b/src/thread.cc index baa659c4d7..eb7a4f727d 100644 --- a/src/thread.cc +++ b/src/thread.cc @@ -9,6 +9,7 @@ #include <list> #include "class_linker.h" +#include "jni_internal.h" #include "object.h" #include "runtime.h" #include "utils.h" @@ -53,20 +54,21 @@ void* ThreadStart(void *arg) { return NULL; } -Thread* Thread::Create(size_t stack_size) { - int prot = PROT_READ | PROT_WRITE; - // TODO: require the stack size to be page aligned? - size_t length = RoundUp(stack_size, 0x1000); - void* stack_limit = mmap(NULL, length, prot, MAP_PRIVATE, -1, 0); - if (stack_limit == MAP_FAILED) { - LOG(FATAL) << "mmap"; - return false; +Thread* Thread::Create(const Runtime* runtime) { + size_t stack_size = runtime->GetStackSize(); + scoped_ptr<MemMap> stack(MemMap::Map(stack_size, PROT_READ | PROT_WRITE)); + if (stack == NULL) { + LOG(FATAL) << "failed to allocate thread stack"; + // notreached + return NULL; } Thread* new_thread = new Thread; new_thread->InitCpu(); - new_thread->stack_limit_ = static_cast<byte*>(stack_limit); - new_thread->stack_base_ = new_thread->stack_limit_ + length; + new_thread->stack_.reset(stack.release()); + // Since stacks are assumed to grown downward the base is the limit and the limit is the base. + new_thread->stack_limit_ = stack->GetAddress(); + new_thread->stack_base_ = stack->GetLimit(); pthread_attr_t attr; int result = pthread_attr_init(&attr); @@ -103,7 +105,7 @@ Thread* Thread::Attach(const Runtime* runtime) { PLOG(FATAL) << "pthread_setspecific failed"; } - JavaVMExt* vm = reinterpret_cast<JavaVMExt*>(runtime->GetJavaVM()); + JavaVMExt* vm = runtime->GetJavaVM(); CHECK(vm != NULL); bool check_jni = vm->check_jni; thread->jni_env_ = reinterpret_cast<JNIEnv*>(new JNIEnvExt(thread, check_jni)); @@ -211,9 +213,9 @@ ThreadList::~ThreadList() { // Make sure that all threads have exited and unregistered when we // reach this point. This means that all daemon threads had been // shutdown cleanly. - CHECK_EQ(list_.size(), 1U); + CHECK_LE(list_.size(), 1U); // TODO: wait for all other threads to unregister - CHECK_EQ(list_.front(), Thread::Current()); + CHECK(list_.size() == 0 || list_.front() == Thread::Current()); // TODO: detach the current thread delete lock_; lock_ = NULL; diff --git a/src/thread.h b/src/thread.h index de5893e848..71fbd48cb9 100644 --- a/src/thread.h +++ b/src/thread.h @@ -7,9 +7,9 @@ #include <list> #include "globals.h" -#include "jni_internal.h" #include "logging.h" #include "macros.h" +#include "mem_map.h" #include "offsets.h" #include "runtime.h" @@ -115,7 +115,7 @@ class Thread { static const size_t kDefaultStackSize = 64 * KB; // Creates a new thread. - static Thread* Create(size_t stack_size); + static Thread* Create(const Runtime* runtime); // Creates a new thread from the calling thread. static Thread* Attach(const Runtime* runtime); @@ -292,6 +292,9 @@ class Thread { // at the next poll. int suspend_count_; + // The memory mapping of the stack for non-attached threads. + scoped_ptr<MemMap> stack_; + // The inclusive base of the control stack. byte* stack_base_; diff --git a/src/unordered_map.h b/src/unordered_map.h index 66613c6a01..44e5417da6 100644 --- a/src/unordered_map.h +++ b/src/unordered_map.h @@ -11,6 +11,7 @@ #include <tr1/unordered_map> #endif +//TODO: move out to stringpiece.h? namespace std { #ifndef __ANDROID__ namespace tr1 { diff --git a/src/unordered_set.h b/src/unordered_set.h new file mode 100644 index 0000000000..18ebaac38a --- /dev/null +++ b/src/unordered_set.h @@ -0,0 +1,12 @@ +// Copyright 2011 Google Inc. All Rights Reserved. + +#ifndef ART_SRC_CLASS_UNORDERED_SET_H_ +#define ART_SRC_CLASS_UNORDERED_SET_H_ + +#ifdef __ANDROID__ +#include <unordered_set> +#else +#include <tr1/unordered_set> +#endif + +#endif // ART_SRC_CLASS_UNORDERED_SET_H_ diff --git a/src/utils_test.cc b/src/utils_test.cc index 1b7315f395..dd411c20c5 100644 --- a/src/utils_test.cc +++ b/src/utils_test.cc @@ -8,7 +8,7 @@ namespace art { -class UtilsTest : public RuntimeTest { +class UtilsTest : public CommonTest { }; TEST(PrettyDescriptorTest, ArrayReferences) { diff --git a/src/zip_archive.cc b/src/zip_archive.cc index fe263baec5..cccaf82d60 100644 --- a/src/zip_archive.cc +++ b/src/zip_archive.cc @@ -386,7 +386,6 @@ bool ZipArchive::MapCentralDirectory() { // It all looks good. Create a mapping for the CD. dir_map_.reset(MemMap::Map(dir_size, PROT_READ, MAP_SHARED, fd_, dir_offset)); if (dir_map_ == NULL) { - LOG(WARNING) << "Zip: cd map failed " << strerror(errno); return false; } diff --git a/src/zip_archive_test.cc b/src/zip_archive_test.cc index 74b3795d82..19f4ee0a33 100644 --- a/src/zip_archive_test.cc +++ b/src/zip_archive_test.cc @@ -11,7 +11,7 @@ namespace art { -class ZipArchiveTest : public RuntimeTest {}; +class ZipArchiveTest : public CommonTest {}; TEST_F(ZipArchiveTest, FindAndExtract) { scoped_ptr<ZipArchive> zip_archive(ZipArchive::Open(GetLibCoreDexFileName())); |