You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
423 lines
13 KiB
423 lines
13 KiB
/*
|
|
* Copyright (C) 2014 The Android Open Source Project
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
#ifndef ART_CMDLINE_CMDLINE_H_
|
|
#define ART_CMDLINE_CMDLINE_H_
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
|
|
#include <fstream>
|
|
#include <iostream>
|
|
#include <string>
|
|
#include <string_view>
|
|
|
|
#include "android-base/stringprintf.h"
|
|
|
|
#include "base/file_utils.h"
|
|
#include "base/logging.h"
|
|
#include "base/mutex.h"
|
|
#include "base/string_view_cpp20.h"
|
|
#include "noop_compiler_callbacks.h"
|
|
#include "runtime.h"
|
|
|
|
#if !defined(NDEBUG)
|
|
#define DBG_LOG LOG(INFO)
|
|
#else
|
|
#define DBG_LOG LOG(DEBUG)
|
|
#endif
|
|
|
|
namespace art {
|
|
|
|
// TODO: Move to <runtime/utils.h> and remove all copies of this function.
|
|
static bool LocationToFilename(const std::string& location, InstructionSet isa,
|
|
std::string* filename) {
|
|
bool has_system = false;
|
|
bool has_cache = false;
|
|
// image_location = /system/framework/boot.art
|
|
// system_image_filename = /system/framework/<image_isa>/boot.art
|
|
std::string system_filename(GetSystemImageFilename(location.c_str(), isa));
|
|
if (OS::FileExists(system_filename.c_str())) {
|
|
has_system = true;
|
|
}
|
|
|
|
bool have_android_data = false;
|
|
bool dalvik_cache_exists = false;
|
|
bool is_global_cache = false;
|
|
std::string dalvik_cache;
|
|
GetDalvikCache(GetInstructionSetString(isa), false, &dalvik_cache,
|
|
&have_android_data, &dalvik_cache_exists, &is_global_cache);
|
|
|
|
std::string cache_filename;
|
|
if (have_android_data && dalvik_cache_exists) {
|
|
// Always set output location even if it does not exist,
|
|
// so that the caller knows where to create the image.
|
|
//
|
|
// image_location = /system/framework/boot.art
|
|
// *image_filename = /data/dalvik-cache/<image_isa>/boot.art
|
|
std::string error_msg;
|
|
if (GetDalvikCacheFilename(location.c_str(), dalvik_cache.c_str(),
|
|
&cache_filename, &error_msg)) {
|
|
has_cache = true;
|
|
}
|
|
}
|
|
if (has_system) {
|
|
*filename = system_filename;
|
|
return true;
|
|
} else if (has_cache) {
|
|
*filename = cache_filename;
|
|
return true;
|
|
} else {
|
|
*filename = system_filename;
|
|
return false;
|
|
}
|
|
}
|
|
|
|
static Runtime* StartRuntime(const char* boot_image_location,
|
|
InstructionSet instruction_set,
|
|
const std::vector<const char*>& runtime_args) {
|
|
CHECK(boot_image_location != nullptr);
|
|
|
|
RuntimeOptions options;
|
|
|
|
// We are more like a compiler than a run-time. We don't want to execute code.
|
|
{
|
|
static NoopCompilerCallbacks callbacks;
|
|
options.push_back(std::make_pair("compilercallbacks", &callbacks));
|
|
}
|
|
|
|
// Boot image location.
|
|
{
|
|
std::string boot_image_option;
|
|
boot_image_option += "-Ximage:";
|
|
boot_image_option += boot_image_location;
|
|
options.push_back(std::make_pair(boot_image_option, nullptr));
|
|
}
|
|
|
|
// Instruction set.
|
|
options.push_back(
|
|
std::make_pair("imageinstructionset",
|
|
reinterpret_cast<const void*>(GetInstructionSetString(instruction_set))));
|
|
|
|
// Explicit runtime args.
|
|
for (const char* runtime_arg : runtime_args) {
|
|
options.push_back(std::make_pair(runtime_arg, nullptr));
|
|
}
|
|
|
|
// None of the command line tools need sig chain. If this changes we'll need
|
|
// to upgrade this option to a proper parameter.
|
|
options.push_back(std::make_pair("-Xno-sig-chain", nullptr));
|
|
if (!Runtime::Create(options, false)) {
|
|
fprintf(stderr, "Failed to create runtime\n");
|
|
return nullptr;
|
|
}
|
|
|
|
// Runtime::Create acquired the mutator_lock_ that is normally given away when we Runtime::Start,
|
|
// give it away now and then switch to a more manageable ScopedObjectAccess.
|
|
Thread::Current()->TransitionFromRunnableToSuspended(kNative);
|
|
|
|
return Runtime::Current();
|
|
}
|
|
|
|
struct CmdlineArgs {
|
|
enum ParseStatus {
|
|
kParseOk, // Parse successful. Do not set the error message.
|
|
kParseUnknownArgument, // Unknown argument. Do not set the error message.
|
|
kParseError, // Parse ok, but failed elsewhere. Print the set error message.
|
|
};
|
|
|
|
bool Parse(int argc, char** argv) {
|
|
// Skip over argv[0].
|
|
argv++;
|
|
argc--;
|
|
|
|
if (argc == 0) {
|
|
fprintf(stderr, "No arguments specified\n");
|
|
PrintUsage();
|
|
return false;
|
|
}
|
|
|
|
std::string error_msg;
|
|
for (int i = 0; i < argc; i++) {
|
|
const char* const raw_option = argv[i];
|
|
const std::string_view option(raw_option);
|
|
if (StartsWith(option, "--boot-image=")) {
|
|
boot_image_location_ = raw_option + strlen("--boot-image=");
|
|
} else if (StartsWith(option, "--instruction-set=")) {
|
|
const char* const instruction_set_str = raw_option + strlen("--instruction-set=");
|
|
instruction_set_ = GetInstructionSetFromString(instruction_set_str);
|
|
if (instruction_set_ == InstructionSet::kNone) {
|
|
fprintf(stderr, "Unsupported instruction set %s\n", instruction_set_str);
|
|
PrintUsage();
|
|
return false;
|
|
}
|
|
} else if (option == "--runtime-arg") {
|
|
if (i + 1 == argc) {
|
|
fprintf(stderr, "Missing argument for --runtime-arg\n");
|
|
PrintUsage();
|
|
return false;
|
|
}
|
|
++i;
|
|
runtime_args_.push_back(argv[i]);
|
|
} else if (StartsWith(option, "--output=")) {
|
|
output_name_ = std::string(option.substr(strlen("--output=")));
|
|
const char* filename = output_name_.c_str();
|
|
out_.reset(new std::ofstream(filename));
|
|
if (!out_->good()) {
|
|
fprintf(stderr, "Failed to open output filename %s\n", filename);
|
|
PrintUsage();
|
|
return false;
|
|
}
|
|
os_ = out_.get();
|
|
} else {
|
|
ParseStatus parse_status = ParseCustom(raw_option, option.length(), &error_msg);
|
|
|
|
if (parse_status == kParseUnknownArgument) {
|
|
fprintf(stderr, "Unknown argument %s\n", option.data());
|
|
}
|
|
|
|
if (parse_status != kParseOk) {
|
|
fprintf(stderr, "%s\n", error_msg.c_str());
|
|
PrintUsage();
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
DBG_LOG << "will call parse checks";
|
|
|
|
{
|
|
ParseStatus checks_status = ParseChecks(&error_msg);
|
|
if (checks_status != kParseOk) {
|
|
fprintf(stderr, "%s\n", error_msg.c_str());
|
|
PrintUsage();
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
virtual std::string GetUsage() const {
|
|
std::string usage;
|
|
|
|
usage += // Required.
|
|
" --boot-image=<file.art>: provide the image location for the boot class path.\n"
|
|
" Do not include the arch as part of the name, it is added automatically.\n"
|
|
" Example: --boot-image=/system/framework/boot.art\n"
|
|
" (specifies /system/framework/<arch>/boot.art as the image file)\n"
|
|
"\n";
|
|
usage += android::base::StringPrintf( // Optional.
|
|
" --instruction-set=(arm|arm64|x86|x86_64): for locating the image\n"
|
|
" file based on the image location set.\n"
|
|
" Example: --instruction-set=x86\n"
|
|
" Default: %s\n"
|
|
"\n",
|
|
GetInstructionSetString(kRuntimeISA));
|
|
usage +=
|
|
" --runtime-arg <argument> used to specify various arguments for the runtime\n"
|
|
" such as initial heap size, maximum heap size, and verbose output.\n"
|
|
" Use a separate --runtime-arg switch for each argument.\n"
|
|
" Example: --runtime-arg -Xms256m\n"
|
|
"\n";
|
|
usage += // Optional.
|
|
" --output=<file> may be used to send the output to a file.\n"
|
|
" Example: --output=/tmp/oatdump.txt\n"
|
|
"\n";
|
|
|
|
return usage;
|
|
}
|
|
|
|
// Specified by --boot-image.
|
|
const char* boot_image_location_ = nullptr;
|
|
// Specified by --instruction-set.
|
|
InstructionSet instruction_set_ = InstructionSet::kNone;
|
|
// Runtime arguments specified by --runtime-arg.
|
|
std::vector<const char*> runtime_args_;
|
|
// Specified by --output.
|
|
std::ostream* os_ = &std::cout;
|
|
std::unique_ptr<std::ofstream> out_; // If something besides cout is used
|
|
std::string output_name_;
|
|
|
|
virtual ~CmdlineArgs() {}
|
|
|
|
bool ParseCheckBootImage(std::string* error_msg) {
|
|
if (boot_image_location_ == nullptr) {
|
|
*error_msg = "--boot-image must be specified";
|
|
return false;
|
|
}
|
|
if (instruction_set_ == InstructionSet::kNone) {
|
|
LOG(WARNING) << "No instruction set given, assuming " << GetInstructionSetString(kRuntimeISA);
|
|
instruction_set_ = kRuntimeISA;
|
|
}
|
|
|
|
DBG_LOG << "boot image location: " << boot_image_location_;
|
|
|
|
// Checks for --boot-image location.
|
|
{
|
|
std::string boot_image_location = boot_image_location_;
|
|
size_t separator_pos = boot_image_location.find(':');
|
|
if (separator_pos != std::string::npos) {
|
|
boot_image_location = boot_image_location.substr(/*pos*/ 0u, /*size*/ separator_pos);
|
|
}
|
|
size_t file_name_idx = boot_image_location.rfind('/');
|
|
if (file_name_idx == std::string::npos) { // Prevent a InsertIsaDirectory check failure.
|
|
*error_msg = "Boot image location must have a / in it";
|
|
return false;
|
|
}
|
|
|
|
// Don't let image locations with the 'arch' in it through, since it's not a location.
|
|
// This prevents a common error "Could not create an image space..." when initing the Runtime.
|
|
if (file_name_idx != std::string::npos) {
|
|
std::string no_file_name = boot_image_location.substr(0, file_name_idx);
|
|
size_t ancestor_dirs_idx = no_file_name.rfind('/');
|
|
|
|
std::string parent_dir_name;
|
|
if (ancestor_dirs_idx != std::string::npos) {
|
|
parent_dir_name = no_file_name.substr(ancestor_dirs_idx + 1);
|
|
} else {
|
|
parent_dir_name = no_file_name;
|
|
}
|
|
|
|
DBG_LOG << "boot_image_location parent_dir_name was " << parent_dir_name;
|
|
|
|
if (GetInstructionSetFromString(parent_dir_name.c_str()) != InstructionSet::kNone) {
|
|
*error_msg = "Do not specify the architecture as part of the boot image location";
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// Check that the boot image location points to a valid file name.
|
|
std::string file_name;
|
|
if (!LocationToFilename(boot_image_location, instruction_set_, &file_name)) {
|
|
*error_msg = android::base::StringPrintf(
|
|
"No corresponding file for location '%s' (filename '%s') exists",
|
|
boot_image_location.c_str(),
|
|
file_name.c_str());
|
|
return false;
|
|
}
|
|
|
|
DBG_LOG << "boot_image_filename does exist: " << file_name;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void PrintUsage() {
|
|
fprintf(stderr, "%s", GetUsage().c_str());
|
|
}
|
|
|
|
protected:
|
|
virtual ParseStatus ParseCustom(const char* raw_option ATTRIBUTE_UNUSED,
|
|
size_t raw_option_length ATTRIBUTE_UNUSED,
|
|
std::string* error_msg ATTRIBUTE_UNUSED) {
|
|
return kParseUnknownArgument;
|
|
}
|
|
|
|
virtual ParseStatus ParseChecks(std::string* error_msg ATTRIBUTE_UNUSED) {
|
|
return kParseOk;
|
|
}
|
|
};
|
|
|
|
template <typename Args = CmdlineArgs>
|
|
struct CmdlineMain {
|
|
int Main(int argc, char** argv) {
|
|
Locks::Init();
|
|
InitLogging(argv, Runtime::Abort);
|
|
std::unique_ptr<Args> args = std::unique_ptr<Args>(CreateArguments());
|
|
args_ = args.get();
|
|
|
|
DBG_LOG << "Try to parse";
|
|
|
|
if (args_ == nullptr || !args_->Parse(argc, argv)) {
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
bool needs_runtime = NeedsRuntime();
|
|
std::unique_ptr<Runtime> runtime;
|
|
|
|
|
|
if (needs_runtime) {
|
|
std::string error_msg;
|
|
if (!args_->ParseCheckBootImage(&error_msg)) {
|
|
fprintf(stderr, "%s\n", error_msg.c_str());
|
|
args_->PrintUsage();
|
|
return EXIT_FAILURE;
|
|
}
|
|
runtime.reset(CreateRuntime(args.get()));
|
|
if (runtime == nullptr) {
|
|
return EXIT_FAILURE;
|
|
}
|
|
if (!ExecuteWithRuntime(runtime.get())) {
|
|
return EXIT_FAILURE;
|
|
}
|
|
} else {
|
|
if (!ExecuteWithoutRuntime()) {
|
|
return EXIT_FAILURE;
|
|
}
|
|
}
|
|
|
|
if (!ExecuteCommon()) {
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
return EXIT_SUCCESS;
|
|
}
|
|
|
|
// Override this function to create your own arguments.
|
|
// Usually will want to return a subtype of CmdlineArgs.
|
|
virtual Args* CreateArguments() {
|
|
return new Args();
|
|
}
|
|
|
|
// Override this function to do something else with the runtime.
|
|
virtual bool ExecuteWithRuntime(Runtime* runtime) {
|
|
CHECK(runtime != nullptr);
|
|
// Do nothing
|
|
return true;
|
|
}
|
|
|
|
// Does the code execution need a runtime? Sometimes it doesn't.
|
|
virtual bool NeedsRuntime() {
|
|
return true;
|
|
}
|
|
|
|
// Do execution without having created a runtime.
|
|
virtual bool ExecuteWithoutRuntime() {
|
|
return true;
|
|
}
|
|
|
|
// Continue execution after ExecuteWith[out]Runtime
|
|
virtual bool ExecuteCommon() {
|
|
return true;
|
|
}
|
|
|
|
virtual ~CmdlineMain() {}
|
|
|
|
protected:
|
|
Args* args_ = nullptr;
|
|
|
|
private:
|
|
Runtime* CreateRuntime(CmdlineArgs* args) {
|
|
CHECK(args != nullptr);
|
|
|
|
return StartRuntime(args->boot_image_location_, args->instruction_set_, args_->runtime_args_);
|
|
}
|
|
};
|
|
} // namespace art
|
|
|
|
#endif // ART_CMDLINE_CMDLINE_H_
|