From dbfe254f5ca96f6c5b2284478597d6140c01a394 Mon Sep 17 00:00:00 2001 From: Andreas Gampe Date: Tue, 25 Nov 2014 22:21:42 -0800 Subject: [PATCH] ART: Print initialization failures to file Add the ability to print boot image initialization failures to a file. Add a tool to convert said file into a Graphviz file. Change-Id: Iedcc337bdf05654c154aa553236f20bdd15572ee --- compiler/driver/compiler_driver.cc | 6 ++ compiler/driver/compiler_options.h | 17 ++++- dex2oat/dex2oat.cc | 14 +++- tools/analyze-init-failures.py | 138 +++++++++++++++++++++++++++++++++++++ 4 files changed, 171 insertions(+), 4 deletions(-) create mode 100755 tools/analyze-init-failures.py diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc index 2e9f8355f..57f38abf5 100644 --- a/compiler/driver/compiler_driver.cc +++ b/compiler/driver/compiler_driver.cc @@ -1820,6 +1820,12 @@ static void InitializeClass(const ParallelCompilationManager* manager, size_t cl mirror::Throwable* exception = soa.Self()->GetException(&throw_location); VLOG(compiler) << "Initialization of " << descriptor << " aborted because of " << exception->Dump(); + std::ostream* file_log = manager->GetCompiler()-> + GetCompilerOptions().GetInitFailureOutput(); + if (file_log != nullptr) { + *file_log << descriptor << "\n"; + *file_log << exception->Dump() << "\n"; + } soa.Self()->ClearException(); transaction.Abort(); CHECK_EQ(old_status, klass->GetStatus()) << "Previous class status not restored"; diff --git a/compiler/driver/compiler_options.h b/compiler/driver/compiler_options.h index 0592f0cf1..aec7d241f 100644 --- a/compiler/driver/compiler_options.h +++ b/compiler/driver/compiler_options.h @@ -17,6 +17,7 @@ #ifndef ART_COMPILER_DRIVER_COMPILER_OPTIONS_H_ #define ART_COMPILER_DRIVER_COMPILER_OPTIONS_H_ +#include #include #include @@ -70,7 +71,8 @@ class CompilerOptions FINAL { #ifdef ART_SEA_IR_MODE sea_ir_mode_(false), #endif - verbose_methods_(nullptr) { + verbose_methods_(nullptr), + init_failure_output_(nullptr) { } CompilerOptions(CompilerFilter compiler_filter, @@ -90,7 +92,8 @@ class CompilerOptions FINAL { #ifdef ART_SEA_IR_MODE bool sea_ir_mode, #endif - const std::vector* verbose_methods + const std::vector* verbose_methods, + std::ostream* init_failure_output ) : // NOLINT(whitespace/parens) compiler_filter_(compiler_filter), huge_method_threshold_(huge_method_threshold), @@ -109,7 +112,8 @@ class CompilerOptions FINAL { #ifdef ART_SEA_IR_MODE sea_ir_mode_(sea_ir_mode), #endif - verbose_methods_(verbose_methods) { + verbose_methods_(verbose_methods), + init_failure_output_(init_failure_output) { } CompilerFilter GetCompilerFilter() const { @@ -217,6 +221,10 @@ class CompilerOptions FINAL { return false; } + std::ostream* GetInitFailureOutput() const { + return init_failure_output_; + } + private: CompilerFilter compiler_filter_; const size_t huge_method_threshold_; @@ -241,6 +249,9 @@ class CompilerOptions FINAL { // Vector of methods to have verbose output enabled for. const std::vector* const verbose_methods_; + // Log initialization of initialization failures to this stream if not null. + std::ostream* const init_failure_output_; + DISALLOW_COPY_AND_ASSIGN(CompilerOptions); }; std::ostream& operator<<(std::ostream& os, const CompilerOptions::CompilerFilter& rhs); diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc index 7d4b72629..a9387bbbb 100644 --- a/dex2oat/dex2oat.cc +++ b/dex2oat/dex2oat.cc @@ -702,6 +702,16 @@ class Dex2Oat FINAL { // on having verbost methods. gLogVerbosity.compiler = false; Split(option.substr(strlen("--verbose-methods=")).ToString(), ',', &verbose_methods_); + } else if (option.starts_with("--dump-init-failures=")) { + std::string file_name = option.substr(strlen("--dump-init-failures=")).data(); + init_failure_output_.reset(new std::ofstream(file_name)); + if (init_failure_output_.get() == nullptr) { + LOG(ERROR) << "Failed to allocate ofstream"; + } else if (init_failure_output_->fail()) { + LOG(ERROR) << "Failed to open " << file_name << " for writing the initialization " + << "failures."; + init_failure_output_.reset(); + } } else { Usage("Unknown argument %s", option.data()); } @@ -906,7 +916,8 @@ class Dex2Oat FINAL { #endif verbose_methods_.empty() ? nullptr : - &verbose_methods_)); + &verbose_methods_, + init_failure_output_.get())); // Done with usage checks, enable watchdog if requested if (watch_dog_enabled) { @@ -1640,6 +1651,7 @@ class Dex2Oat FINAL { std::string profile_file_; // Profile file to use TimingLogger* timings_; std::unique_ptr compiler_phases_timings_; + std::unique_ptr init_failure_output_; DISALLOW_IMPLICIT_CONSTRUCTORS(Dex2Oat); }; diff --git a/tools/analyze-init-failures.py b/tools/analyze-init-failures.py new file mode 100755 index 000000000..f803ea380 --- /dev/null +++ b/tools/analyze-init-failures.py @@ -0,0 +1,138 @@ +#!/usr/bin/env python +# +# 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. + +"""Analyzes the dump of initialization failures and creates a Graphviz dot file + representing dependencies.""" + +import codecs +import os +import re +import string +import sys + + +_CLASS_RE = re.compile(r'^L(.*);$') +_ERROR_LINE_RE = re.compile(r'^java.lang.InternalError: (.*)') +_STACK_LINE_RE = re.compile(r'^\s*at\s[^\s]*\s([^\s]*)') + +def Confused(filename, line_number, line): + sys.stderr.write('%s:%d: confused by:\n%s\n' % (filename, line_number, line)) + raise Exception("giving up!") + sys.exit(1) + + +def ProcessFile(filename): + lines = codecs.open(filename, 'r', 'utf8', 'replace').read().split('\n') + it = iter(lines) + + class_fail_class = {} + class_fail_method = {} + root_failures = set() + root_errors = {} + + while True: + try: + # We start with a class descriptor. + raw_line = it.next() + m = _CLASS_RE.search(raw_line) + # print(raw_line) + if m is None: + continue + # Found a class. + failed_clazz = m.group(1).replace('/','.') + # print('Is a class %s' % failed_clazz) + # The error line should be next. + raw_line = it.next() + m = _ERROR_LINE_RE.search(raw_line) + # print(raw_line) + if m is None: + Confused(filename, -1, raw_line) + continue + # Found an error line. + error = m.group(1) + # print('Is an error %s' % error) + # Get the top of the stack + raw_line = it.next() + m = _STACK_LINE_RE.search(raw_line) + if m is None: + continue + # Found a stack line. Get the method. + method = m.group(1) + # print('Is a stack element %s' % method) + (left_of_paren,paren,right_of_paren) = method.partition('(') + (root_err_class,dot,root_method_name) = left_of_paren.rpartition('.') + # print('Error class %s' % err_class) + # print('Error method %s' % method_name) + # Record the root error. + root_failures.add(root_err_class) + # Parse all the trace elements to find the "immediate" cause. + immediate_class = root_err_class + immediate_method = root_method_name + root_errors[root_err_class] = error + # Now go "up" the stack. + while True: + raw_line = it.next() + m = _STACK_LINE_RE.search(raw_line) + if m is None: + break # Nothing more to see here. + method = m.group(1) + (left_of_paren,paren,right_of_paren) = method.partition('(') + (err_class,dot,err_method_name) = left_of_paren.rpartition('.') + if err_method_name == "": + # A class initializer is on the stack... + class_fail_class[err_class] = immediate_class + class_fail_method[err_class] = immediate_method + immediate_class = err_class + immediate_method = err_method_name + except StopIteration: + # print('Done') + break # Done + + # Assign IDs. + fail_sources = set(class_fail_class.values()); + all_classes = fail_sources | set(class_fail_class.keys()) + i = 0 + class_index = {} + for clazz in all_classes: + class_index[clazz] = i + i = i + 1 + + # Now create the nodes. + for (r_class, r_id) in class_index.items(): + error_string = '' + if r_class in root_failures: + error_string = ',color=Red,tooltip="' + root_errors[r_class] + '",URL="' + root_errors[r_class] + '"' + print(' n%d [shape=box,label="%s"%s];' % (r_id, r_class, error_string)) + + # Some space. + print('') + + # Connections. + for (failed_class,error_class) in class_fail_class.items(): + print(' n%d -> n%d;' % (class_index[failed_class], class_index[error_class])) + + +def main(): + print('digraph {') + print(' overlap=false;') + print(' splines=true;') + ProcessFile(sys.argv[1]) + print('}') + sys.exit(0) + + +if __name__ == '__main__': + main() -- 2.11.0