summaryrefslogtreecommitdiffstats
path: root/cmdline
diff options
context:
space:
mode:
authorIgor Murashkin <iam@google.com>2014-11-13 14:38:00 -0800
committerIgor Murashkin <iam@google.com>2014-12-16 12:00:59 -0800
commit3774335b08076117d6950cd472cdd59a167470b5 (patch)
treec2d24814e2d829f55ae25415b5b578cc040e0934 /cmdline
parent5b6912e455da798a79a4bb3235e276351b38ab58 (diff)
downloadart-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.h370
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_