Work on option parsing as prelude to image loading
Change-Id: I13edbd9b341e603817941beaca676535a7e590c7
diff --git a/src/common_test.h b/src/common_test.h
index 9f8d6df..1fbe718 100644
--- a/src/common_test.h
+++ b/src/common_test.h
@@ -333,7 +333,7 @@
java_lang_dex_file_.reset(GetLibCoreDex());
- std::vector<DexFile*> boot_class_path;
+ std::vector<const DexFile*> boot_class_path;
boot_class_path.push_back(java_lang_dex_file_.get());
runtime_.reset(Runtime::Create(boot_class_path));
diff --git a/src/heap.h b/src/heap.h
index 698535f..247c6ba 100644
--- a/src/heap.h
+++ b/src/heap.h
@@ -18,7 +18,7 @@
class Heap {
public:
- static const size_t kStartupSize = 16 * MB;
+ static const size_t kInitialSize = 16 * MB;
static const size_t kMaximumSize = 64 * MB;
diff --git a/src/image_test.cc b/src/image_test.cc
index 5de860e..c3662cd 100644
--- a/src/image_test.cc
+++ b/src/image_test.cc
@@ -20,8 +20,26 @@
ImageWriter writer;
ScratchFile tmp;
- bool success = writer.Write(spaces[0], tmp.GetFilename(), reinterpret_cast<byte*>(0x5000000));
+ const int image_base = 0x5000000;
+ bool success = writer.Write(spaces[0], tmp.GetFilename(), reinterpret_cast<byte*>(image_base));
EXPECT_TRUE(success);
+
+ // tear down old runtime and make a new one
+ delete runtime_.release();
+ java_lang_dex_file_.reset(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("bootclasspath", &boot_class_path));
+ std::string boot_image("-Xbootimage:");
+ boot_image.append(tmp.GetFilename());
+ options.push_back(std::make_pair(boot_image.c_str(), reinterpret_cast<void*>(NULL)));
+
+ runtime_.reset(Runtime::Create(options, false));
+ ASSERT_TRUE(runtime_ != NULL);
+ class_linker_ = runtime_->GetClassLinker();
}
} // namespace art
diff --git a/src/runtime.cc b/src/runtime.cc
index 05a89b2..411ed67 100644
--- a/src/runtime.cc
+++ b/src/runtime.cc
@@ -4,6 +4,7 @@
#include <cstdio>
#include <cstdlib>
+#include <limits>
#include <vector>
#include "class_linker.h"
@@ -72,21 +73,73 @@
}
}
-// TODO: move option processing elsewhere.
-const char* FindBootClassPath(const Runtime::Options& options) {
- const char* boot_class_path = getenv("BOOTCLASSPATH");
- const char* flag = "-Xbootclasspath:";
- for (size_t i = 0; i < options.size(); ++i) {
- const StringPiece& option = options[i].first;
- if (option.starts_with(flag)) {
- boot_class_path = option.substr(strlen(flag)).data();
+
+
+// Parse a string of the form /[0-9]+[kKmMgG]?/, which is used to specify
+// memory sizes. [kK] indicates kilobytes, [mM] megabytes, and
+// [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.
+//
+// The spec says the -Xmx and -Xms options must be multiples of 1024. It
+// doesn't say anything about -Xss.
+//
+// Returns 0 (a useless size) if "s" is malformed or specifies a low or
+// non-evenly-divisible value.
+//
+size_t ParseMemoryOption(const char *s, size_t div) {
+ // strtoul accepts a leading [+-], which we don't want,
+ // so make sure our string starts with a decimal digit.
+ if (isdigit(*s)) {
+ const char *s2;
+ size_t val = strtoul(s, (char **)&s2, 10);
+ if (s2 != s) {
+ // s2 should be pointing just after the number.
+ // If this is the end of the string, the user
+ // has specified a number of bytes. Otherwise,
+ // 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);
+ }
+ } 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) {
+ return val;
+ }
}
}
- if (boot_class_path == NULL) {
- return "";
- } else {
- return boot_class_path;
- }
+ return 0;
}
DexFile* Open(const std::string& filename) {
@@ -102,35 +155,67 @@
}
}
-void CreateBootClassPath(const Runtime::Options& options,
- std::vector<DexFile*>* boot_class_path) {
- CHECK(boot_class_path != NULL);
- const char* str = FindBootClassPath(options);
+void CreateBootClassPath(const char* boot_class_path_cstr,
+ std::vector<DexFile*>& boot_class_path_vector) {
+ CHECK(boot_class_path_cstr != NULL);
std::vector<std::string> parsed;
- ParseClassPath(str, &parsed);
+ ParseClassPath(boot_class_path_cstr, &parsed);
for (size_t i = 0; i < parsed.size(); ++i) {
DexFile* dex_file = Open(parsed[i]);
if (dex_file != NULL) {
- boot_class_path->push_back(dex_file);
+ boot_class_path_vector.push_back(dex_file);
}
}
}
-// TODO: do something with ignore_unrecognized when we parse the option
-// strings for real.
-Runtime* Runtime::Create(const Options& options, bool ignore_unrecognized) {
- std::vector<DexFile*> boot_class_path;
- CreateBootClassPath(options, &boot_class_path);
- return Runtime::Create(boot_class_path);
+Runtime::ParsedOptions::ParsedOptions(const Options& options, bool ignore_unrecognized) {
+ const char* boot_class_path = getenv("BOOTCLASSPATH");
+ boot_image_ = NULL;
+ heap_initial_size_ = Heap::kInitialSize;
+ heap_maximum_size_ = Heap::kMaximumSize;
+
+ for (size_t i = 0; i < options.size(); ++i) {
+ const StringPiece& option = options[i].first;
+ // TODO parse -D, -verbose, vprintf, exit, abort
+ if (option.starts_with("-Xbootclasspath:")) {
+ boot_class_path = option.substr(strlen("-Xbootclasspath:")).data();
+ } else if (option == "bootclasspath") {
+ boot_class_path_ = *reinterpret_cast<const std::vector<DexFile*>*>(options[i].second);
+ } else if (option.starts_with("-Xbootimage:")) {
+ boot_image_ = option.substr(strlen("-Xbootimage:")).data();
+ } else if (option.starts_with("-Xms")) {
+ heap_initial_size_ = ParseMemoryOption(option.substr(strlen("-Xms")).data(), 1024);
+ } else if (option.starts_with("-Xmx")) {
+ heap_maximum_size_ = ParseMemoryOption(option.substr(strlen("-Xmx")).data(), 1024);
+ } else {
+ if (!ignore_unrecognized) {
+ // TODO: indicate error for JNI_CreateJavaVM and print usage via vfprintf
+ LOG(FATAL) << "Unrecognized option " << option;
+ }
+ }
+ }
+
+ if (boot_class_path == NULL) {
+ boot_class_path = "";
+ }
+ if (boot_class_path_.size() == 0) {
+ CreateBootClassPath(boot_class_path, boot_class_path_);
+ }
}
-Runtime* Runtime::Create(const std::vector<DexFile*>& boot_class_path) {
+Runtime* Runtime::Create(const std::vector<const DexFile*>& boot_class_path) {
+ Runtime::Options options;
+ options.push_back(std::make_pair("bootclasspath", &boot_class_path));
+ return Runtime::Create(options, false);
+}
+
+Runtime* Runtime::Create(const Options& options, bool ignore_unrecognized) {
// TODO: acquire a static mutex on Runtime to avoid racing.
if (Runtime::instance_ != NULL) {
return NULL;
}
scoped_ptr<Runtime> runtime(new Runtime());
- bool success = runtime->Init(boot_class_path);
+ bool success = runtime->Init(options, ignore_unrecognized);
if (!success) {
return NULL;
} else {
@@ -138,14 +223,16 @@
}
}
-bool Runtime::Init(const std::vector<DexFile*>& boot_class_path) {
+bool Runtime::Init(const Options& options, bool ignore_unrecognized) {
+ ParsedOptions parsed_options(options, ignore_unrecognized);
CHECK_EQ(kPageSize, sysconf(_SC_PAGE_SIZE));
thread_list_ = ThreadList::Create();
- Heap::Init(Heap::kStartupSize, Heap::kMaximumSize);
+ Heap::Init(parsed_options.heap_initial_size_,
+ parsed_options.heap_maximum_size_);
Thread::Init();
Thread* current_thread = Thread::Attach();
thread_list_->Register(current_thread);
- class_linker_ = ClassLinker::Create(boot_class_path);
+ class_linker_ = ClassLinker::Create(parsed_options.boot_class_path_);
java_vm_.reset(CreateJavaVM(this));
return true;
}
diff --git a/src/runtime.h b/src/runtime.h
index 56a8970..9ef7deb 100644
--- a/src/runtime.h
+++ b/src/runtime.h
@@ -21,11 +21,22 @@
class Runtime {
public:
- typedef std::vector<std::pair<StringPiece, void*> > Options;
+
+ typedef std::vector<std::pair<StringPiece, const void*> > Options;
+
+ class ParsedOptions {
+ public:
+ ParsedOptions(const Options& options, bool ignore_unrecognized);
+
+ std::vector<DexFile*> boot_class_path_;
+ const char* boot_image_;
+ size_t heap_initial_size_;
+ size_t heap_maximum_size_;
+ };
// Creates and initializes a new runtime.
static Runtime* Create(const Options& options, bool ignore_unrecognized);
- static Runtime* Create(const std::vector<DexFile*>& boot_class_path);
+ static Runtime* Create(const std::vector<const DexFile*>& boot_class_path);
static Runtime* Current() {
return instance_;
@@ -70,7 +81,7 @@
Runtime() : thread_list_(NULL), class_linker_(NULL) {}
// Initializes a new uninitialized runtime.
- bool Init(const std::vector<DexFile*>& boot_class_path);
+ bool Init(const Options& options, bool ignore_unrecognized);
ThreadList* thread_list_;