diff options
author | Igor Murashkin <iam@google.com> | 2014-11-13 14:38:00 -0800 |
---|---|---|
committer | Igor Murashkin <iam@google.com> | 2014-12-16 12:00:59 -0800 |
commit | 3774335b08076117d6950cd472cdd59a167470b5 (patch) | |
tree | c2d24814e2d829f55ae25415b5b578cc040e0934 /cmdline | |
parent | 5b6912e455da798a79a4bb3235e276351b38ab58 (diff) | |
download | art-3774335b08076117d6950cd472cdd59a167470b5.zip art-3774335b08076117d6950cd472cdd59a167470b5.tar.gz art-3774335b08076117d6950cd472cdd59a167470b5.tar.bz2 |
Add a new imgdiag tool to diff boot.art/core.art against a process
Analyze the dirty memory pages of a running process per-object,
this allows is to to fine-tune the dirty object binning algorithm in
image writer.
Also:
* Factor out oatdump command line parsing code into cmdline.h
* Factor out common build rules for building variations of binaries
* Add a gtest for imgdiag
Bug: 17611661
Change-Id: I3ac852a0d223af66f6d59ae5dbc3df101475e3d0
Diffstat (limited to 'cmdline')
-rw-r--r-- | cmdline/cmdline.h | 370 |
1 files changed, 370 insertions, 0 deletions
diff --git a/cmdline/cmdline.h b/cmdline/cmdline.h new file mode 100644 index 0000000..c15594a --- /dev/null +++ b/cmdline/cmdline.h @@ -0,0 +1,370 @@ +/* + * 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 "runtime.h" +#include "base/stringpiece.h" +#include "noop_compiler_callbacks.h" +#include "base/logging.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 { + return false; + } +} + +static Runtime* StartRuntime(const char* boot_image_location, + InstructionSet instruction_set) { + 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.c_str(), nullptr)); + } + + // Instruction set. + options.push_back( + std::make_pair("imageinstructionset", + reinterpret_cast<const void*>(GetInstructionSetString(instruction_set)))); + + 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 StringPiece option(argv[i]); + if (option.starts_with("--boot-image=")) { + boot_image_location_ = option.substr(strlen("--boot-image=")).data(); + } else if (option.starts_with("--instruction-set=")) { + StringPiece instruction_set_str = option.substr(strlen("--instruction-set=")).data(); + instruction_set_ = GetInstructionSetFromString(instruction_set_str.data()); + if (instruction_set_ == kNone) { + fprintf(stderr, "Unsupported instruction set %s\n", instruction_set_str.data()); + PrintUsage(); + return false; + } + } else if (option.starts_with("--output=")) { + output_name_ = option.substr(strlen("--output=")).ToString(); + 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(option, &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" + "\n"; + usage += StringPrintf( // Optional. + " --instruction-set=(arm|arm64|mips|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 += // 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_ = kRuntimeISA; + // 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() {} + + protected: + virtual ParseStatus ParseCustom(const StringPiece& option, std::string* error_msg) { + UNUSED(option); + UNUSED(error_msg); + + return kParseUnknownArgument; + } + + virtual ParseStatus ParseChecks(std::string* error_msg) { + if (boot_image_location_ == nullptr) { + *error_msg = "--boot-image must be specified"; + return kParseError; + } + + DBG_LOG << "boot image location: " << boot_image_location_; + + // Checks for --boot-image location. + { + std::string boot_image_location = boot_image_location_; + 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 kParseError; + } + + // 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()) != kNone) { + *error_msg = "Do not specify the architecture as part of the boot image location"; + return kParseError; + } + } + + // 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 = StringPrintf("No corresponding file for location '%s' exists", + file_name.c_str()); + return kParseError; + } + + DBG_LOG << "boot_image_filename does exist: " << file_name; + } + + return kParseOk; + } + + private: + void PrintUsage() { + fprintf(stderr, "%s", GetUsage().c_str()); + } +}; + +template <typename Args = CmdlineArgs> +struct CmdlineMain { + int Main(int argc, char** argv) { + InitLogging(argv); + 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; + } + + std::unique_ptr<Runtime> runtime = CreateRuntime(args.get()); + if (runtime == nullptr) { + return EXIT_FAILURE; + } + + bool needs_runtime = NeedsRuntime(); + + if (needs_runtime) { + 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: + std::unique_ptr<Runtime> CreateRuntime(CmdlineArgs* args) { + CHECK(args != nullptr); + + return std::unique_ptr<Runtime>(StartRuntime(args->boot_image_location_, + args->instruction_set_)); + } +}; +} // namespace art + +#endif // ART_CMDLINE_CMDLINE_H_ |