From b59f811f2310e9abe03cfac5ad1023254708f969 Mon Sep 17 00:00:00 2001 From: Peter Kubov Date: Thu, 23 Jul 2020 09:29:10 +0200 Subject: [PATCH] Provide unified logging interface (#816) * retdec::io: new logging interface Provides new logging interface that is ment to unify output that is produced in each part of RetDec decompiler. The interface is designed to provide eazy management of logging into files/tty. Currently each module od decompiler manages logging on its own. This is not sustainable state as change to logging interface is rather difficult. * Use retdec::io interface for logging * Get rid of llvm-support module This module is now obsolete as all logging mechanisms have been transferred to retdec::io module. * Provide option [-s|--silent] Provides new option [-s|--silent] for retdec-decompiler executable. This option will force retdec not to output anything on stdout. * retdec/config: provide option to specify Log/Error files Provides way to specify Log/Error files to RetDec. This is useful for RetDec plugins that might this way control decompilation output. * Create loggers from user configuration * Fix validators error messages Fixes bug provided by fbbff343. * Change Log functions to return new object Provides change to log interface. Before this commit logging was done on global objects. Now the logging should be made on temporary objects that are returned from special functions. The reason for this is for usage of colors on output. When temporary object is destructed default color is printed on required interface. * Change namespace Log to class Log This way we can avoid not intentional usage of the Log interface. All functions of Log are ment to be used like: Log::info() Log::debug() Log::error() * Provide missing doxygen * Fix missing cassert header Fixes dc5791724. * Enable ANSI colors on Windows Starting Windows aniversary update Windows terminals provide supprot for ANSI colors. This, however, needs to be enabled manually. More about this can be found here: https://docs.microsoft.com/en-us/windows/console/console-virtual-terminal-sequences?redirectedfrom=MSDN * utils/io/logger: don't print colors to not supported terminals * Use macro OS_WINDOWS instead of __has_include Provides substitution of __has_include macro to OS_WINDOWS defined in utils/os.h. The reason for this is to maintain a level of consistency in RetDec source code base. * Add a CHANGELOG entry for #791 --- CHANGELOG.md | 1 + cmake/options.cmake | 1 - include/retdec/config/parameters.h | 8 +- include/retdec/llvm-support/diagnostics.h | 132 ------------ include/retdec/llvmir2hll/llvmir2hll.h | 1 - .../llvmir2hll/optimizer/optimizer_manager.h | 1 - .../cli_pattern_finder_runner.h | 5 +- .../retdec/llvmir2hll/validator/validator.h | 10 +- include/retdec/unpacker/plugin.h | 12 +- include/retdec/utils/io/log.h | 142 +++++++++++++ include/retdec/utils/io/logger.h | 107 ++++++++++ src/CMakeLists.txt | 1 - src/ar-extractortool/ar_extractor.cpp | 20 +- src/bin2llvmir/CMakeLists.txt | 1 - src/bin2llvmir/analyses/ctor_dtor.cpp | 2 - .../analyses/reaching_definitions.cpp | 1 - src/bin2llvmir/analyses/symbolic_tree.cpp | 1 - .../asm_inst_remover/asm_inst_remover.cpp | 2 - .../class_hierarchy/hierarchy_analysis.cpp | 2 - .../optimizations/constants/constants.cpp | 1 - .../optimizations/decoder/decoder.cpp | 4 +- .../optimizations/idioms/idioms.cpp | 2 - .../idioms/idioms_magicdivmod.cpp | 2 - .../idioms_libgcc/idioms_libgcc.cpp | 1 - .../param_return/param_return.cpp | 1 - .../provider_init/provider_init.cpp | 5 +- .../select_functions/select_functions.cpp | 1 - .../simple_types/simple_types.cpp | 1 - .../stack_pointer_ops/stack_pointer_ops.cpp | 1 - src/bin2llvmir/optimizations/syscalls/arm.cpp | 2 - .../optimizations/syscalls/mips.cpp | 2 - src/bin2llvmir/optimizations/syscalls/x86.cpp | 2 - .../types_propagator/types_propagator.cpp | 10 +- .../unreachable_funcs/unreachable_funcs.cpp | 1 - .../optimizations/writer_dsm/writer_dsm.cpp | 1 - .../optimizations/x87_fpu/x87_fpu.cpp | 2 + src/bin2llvmir/providers/config.cpp | 2 - src/bin2llvmir/providers/lti.cpp | 1 - src/bin2llvmir/retdec-bin2llvmir-config.cmake | 1 - src/bin2llvmir/utils/ir_modifier.cpp | 2 - src/bin2pat/bin2pat.cpp | 26 +-- src/capstone2llvmir/arm/arm.cpp | 1 - src/capstone2llvmir/arm64/arm64.cpp | 9 +- src/capstone2llvmir/capstone2llvmir_impl.cpp | 1 - src/capstone2llvmir/llvmir_utils.cpp | 2 - src/capstone2llvmir/mips/mips.cpp | 1 - src/capstone2llvmir/powerpc/powerpc.cpp | 1 - src/capstone2llvmir/x86/x86.cpp | 1 - src/capstone2llvmirtool/capstone2llvmir.cpp | 48 ++--- src/common/address.cpp | 1 - src/config/parameters.cpp | 26 +++ src/debugformat/debugformat.cpp | 1 - src/debugformat/dwarf.cpp | 2 - src/debugformat/pdb.cpp | 2 - src/demanglertool/demangler.cpp | 12 +- src/fileformat/file_format/file_format.cpp | 9 +- .../dotnet_type_reconstructor.cpp | 2 - .../types/resource_table/resource_table.cpp | 1 - src/fileformat/utils/byte_array_buffer.cpp | 2 - .../file_presentation/json_presentation.cpp | 6 +- .../file_presentation/plain_presentation.cpp | 158 +++++++------- src/fileinfo/fileinfo.cpp | 11 +- src/getsig/getsig.cpp | 13 +- src/idr2pat/idr2pat.cpp | 7 +- src/llvm-support/CMakeLists.txt | 58 ------ src/llvm-support/diagnostics.cpp | 116 ----------- .../retdec-llvm-support-config.cmake | 11 - src/llvmir-emul/llvmir_emul.cpp | 2 - src/llvmir2hll/CMakeLists.txt | 1 - .../non_recursive_cfg_builder.cpp | 13 +- .../hll/hll_writers/c_hll_writer.cpp | 3 - src/llvmir2hll/llvm/llvmir2bir_converter.cpp | 8 +- .../llvmir2bir_converter/labels_handler.cpp | 2 - src/llvmir2hll/llvmir2hll.cpp | 193 ++++++++---------- .../obtainer/call_info_obtainer.cpp | 7 +- .../optim_call_info_obtainer.cpp | 44 ++-- .../optimizer/optimizer_manager.cpp | 7 +- .../cli_pattern_finder_runner.cpp | 14 +- .../api_call_seq_pattern_finder.cpp | 10 +- src/llvmir2hll/retdec-llvmir2hll-config.cmake | 1 - .../semantics/compound_semantics_builder.cpp | 9 +- .../break_outside_loop_validator.cpp | 12 +- .../no_global_var_def_validator.cpp | 6 +- .../validator/validators/return_validator.cpp | 12 +- src/macho-extractor/break_fat.cpp | 5 +- src/macho-extractortool/macho_extractor.cpp | 27 ++- src/pat2yara/pat2yara.cpp | 18 +- src/pdbparser/pdb_types.cpp | 1 - src/pelib/ResourceDirectory.cpp | 28 +-- src/retdec-decompiler/retdec-decompiler.cpp | 78 +++++-- src/retdec/retdec.cpp | 46 ++++- src/retdectool/retdec.cpp | 42 ++-- src/stacofin/stacofin.cpp | 1 - src/stacofintool/stacofin.cpp | 21 +- src/unpackertool/plugin_mgr.cpp | 1 - src/unpackertool/unpacker.cpp | 33 +-- src/utils/CMakeLists.txt | 2 + src/utils/io/log.cpp | 76 +++++++ src/utils/io/logger.cpp | 157 ++++++++++++++ support/decompiler-config.json | 2 +- .../cli_pattern_finder_runner_tests.cpp | 20 +- 101 files changed, 1079 insertions(+), 846 deletions(-) delete mode 100644 include/retdec/llvm-support/diagnostics.h create mode 100644 include/retdec/utils/io/log.h create mode 100644 include/retdec/utils/io/logger.h delete mode 100644 src/llvm-support/CMakeLists.txt delete mode 100644 src/llvm-support/diagnostics.cpp delete mode 100644 src/llvm-support/retdec-llvm-support-config.cmake create mode 100644 src/utils/io/log.cpp create mode 100644 src/utils/io/logger.cpp diff --git a/CHANGELOG.md b/CHANGELOG.md index 9e56ff895..9dd10e573 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ # dev +* Enhancement: Unified logging on stdout/stderr. Added option `--silent`. Printed text is colored only when output is a terminal ([#791](https://github.com/avast/retdec/issues/791). * Enhancement: Support all the CMake build types (i.e. `Debug`, `Release`, `RelWithDebInfo` and `MinSizeRel`) on all systems ([#774](https://github.com/avast/retdec/issues/774)). * Enhancement: YARA updated to version 4.0.1 ([#758](https://github.com/avast/retdec/issues/758)), fixed Mach-O parsing issue ([#283](https://github.com/avast/retdec/issues/283)). * Enhancement: Improved detection of many packers/installers/compilers in `retdec-fileinfo`, including Armadillo ([#733](https://github.com/avast/retdec/pull/733)), VMProtect ([#734](https://github.com/avast/retdec/pull/734), [#778](https://github.com/avast/retdec/pull/778)), Petite ([#735](https://github.com/avast/retdec/pull/735)), Enigma ([#741](https://github.com/avast/retdec/pull/741)), ASPack ([#743](https://github.com/avast/retdec/pull/743)), Eziriz ([#746](https://github.com/avast/retdec/pull/746)), PyInstaller ([#748](https://github.com/avast/retdec/pull/748)), Astrum InstallWizard ([#753](https://github.com/avast/retdec/pull/753)), AutoHotKey ([#756](https://github.com/avast/retdec/pull/756)), AutoIt ([#757](https://github.com/avast/retdec/pull/757)), BAT to PE-EXE script compilers ([#761](https://github.com/avast/retdec/pull/761)), Bero ([#764](https://github.com/avast/retdec/pull/764)), CExe ([#781](https://github.com/avast/retdec/pull/781)), MoleBox ([#815](https://github.com/avast/retdec/pull/815)), and other improvements ([#804](https://github.com/avast/retdec/pull/804)). diff --git a/cmake/options.cmake b/cmake/options.cmake index 6cf65382d..6f5980020 100644 --- a/cmake/options.cmake +++ b/cmake/options.cmake @@ -84,7 +84,6 @@ foreach(t ${RETDEC_ENABLE}) set_if_equal(${t} "fileinfo" RETDEC_ENABLE_FILEINFO) set_if_equal(${t} "getsig" RETDEC_ENABLE_GETSIG) set_if_equal(${t} "idr2pat" RETDEC_ENABLE_IDR2PAT) - set_if_equal(${t} "llvm-support" RETDEC_ENABLE_LLVM_SUPPORT) set_if_equal(${t} "llvmir-emul" RETDEC_ENABLE_LLVMIR_EMUL) set_if_equal(${t} "llvmir2hll" RETDEC_ENABLE_LLVMIR2HLL) set_if_equal(${t} "loader" RETDEC_ENABLE_LOADER) diff --git a/include/retdec/config/parameters.h b/include/retdec/config/parameters.h index 9a6fcc517..65d4f12aa 100644 --- a/include/retdec/config/parameters.h +++ b/include/retdec/config/parameters.h @@ -60,6 +60,8 @@ class Parameters void setOutputConfigFile(const std::string& file); void setOutputUnpackedFile(const std::string& file); void setOutputFormat(const std::string& format); + void setLogFile(const std::string& file); + void setErrFile(const std::string& file); void setMaxMemoryLimit(uint64_t limit); void setIsMaxMemoryLimitHalfRam(bool f); void setTimeout(uint64_t seconds); @@ -95,6 +97,8 @@ class Parameters const std::string& getOutputConfigFile() const; const std::string& getOutputUnpackedFile() const; const std::string& getOutputFormat() const; + const std::string& getLogFile() const; + const std::string& getErrFile() const; uint64_t getMaxMemoryLimit() const; uint64_t getTimeout() const; retdec::common::Address getEntryPoint() const; @@ -136,7 +140,7 @@ class Parameters private: /// Decompilation will verbosely inform about the /// decompilation process. - bool _verboseOutput = false; + bool _verboseOutput = true; /// Keep all functions in the decompiler's output. /// Otherwise, only functions reachable from main are kept. @@ -158,6 +162,8 @@ class Parameters std::string _outputConfigFile; std::string _outputUnpackedFile; std::string _outputFormat; + std::string _logFile; + std::string _errFile; uint64_t _maxMemoryLimit = 0; bool _maxMemoryLimitHalfRam = true; uint64_t _timeout = 0; diff --git a/include/retdec/llvm-support/diagnostics.h b/include/retdec/llvm-support/diagnostics.h deleted file mode 100644 index 1ecab7e44..000000000 --- a/include/retdec/llvm-support/diagnostics.h +++ /dev/null @@ -1,132 +0,0 @@ -/** -* @file include/retdec/llvm-support/diagnostics.h -* @brief Functions concerning emission of diagnostics messages, like error or -* warning messages. -* @copyright (c) 2017 Avast Software, licensed under the MIT license -*/ - -#ifndef RETDEC_LLVM_SUPPORT_DIAGNOSTICS_H -#define RETDEC_LLVM_SUPPORT_DIAGNOSTICS_H - -#include - -#include - -namespace retdec { -namespace llvm_support { - -/// @name Internal (For Internal Use Only) -/// @{ - -/** -* @brief A base case for printInto(). For internal use only. -*/ -inline void printInto(llvm::raw_ostream &) {} - -/** -* @brief Prints the given arguments to the given stream. For internal use only. -*/ -template -void printInto(llvm::raw_ostream &stream, T &&arg, Args &&... args) { - stream << arg; - printInto(stream, std::forward(args)...); -} - -/** -* @brief Prints a colored line with the given arguments to the given stream. -* For internal use only. -*/ -template -void printColoredLine(llvm::raw_ostream &stream, llvm::raw_ostream::Colors color, - bool bold, Args &&... args) { - stream.changeColor(color, bold); - printInto(stream, std::forward(args)...); - stream.resetColor(); - stream << "\n"; -} - -/// @} - -/// @name Phases -/// @{ - -void printPhase( - const std::string &phaseName, - bool print = true, - llvm::raw_ostream &stream = llvm::outs() -); -void printSubPhase( - const std::string &phaseName, - bool print = true, - llvm::raw_ostream &stream = llvm::outs() -); -void printSubSubPhase( - const std::string &phaseName, - bool print = true, - llvm::raw_ostream &stream = llvm::outs() -); -void printSubSubSubPhase( - const std::string &phaseName, - bool print = true, - llvm::raw_ostream &stream = llvm::outs() -); - -/// @} - -/// @name Messages -/// @{ - -/** -* @brief Prints the given error message to the standard error. -* -* A new line is automatically appended after the message. -* -* Usage example: -* @code -* printErrorMessage("Function ", funcName, " does not exist.")); -* @endcode -*/ -template -void printErrorMessage(const std::string &message, Args &&... args) { - printColoredLine(llvm::errs(), llvm::raw_ostream::RED, /* bold */ false, - "Error: ", message, std::forward(args)...); -} - -/** -* @brief Prints the given warning message to the standard error. -* -* A new line is automatically appended after the message. -* -* Usage example: -* @code -* printWarningMessage("Function ", funcName, " does not exist.")); -* @endcode -*/ -template -void printWarningMessage(const std::string &message, Args &&... args) { - printColoredLine(llvm::errs(), llvm::raw_ostream::CYAN, /* bold */ false, - "Warning: ", message, std::forward(args)...); -} - -/** -* @brief Prints the given info message to the standard error. -* -* A new line is automatically appended after the message. -* -* Usage example: -* @code -* printInfoMessage("Function ", funcName, " does not exist.")); -* @endcode -*/ -template -void printInfoMessage(const std::string &message, Args &&... args) { - printColoredLine(llvm::errs(), llvm::raw_ostream::GREEN, /* bold */ false, - "Info: ", message, std::forward(args)...); -} - -/// @} - -} // namespace llvm_support -} // namespace retdec - -#endif diff --git a/include/retdec/llvmir2hll/llvmir2hll.h b/include/retdec/llvmir2hll/llvmir2hll.h index 7edcb25fa..224ea5278 100644 --- a/include/retdec/llvmir2hll/llvmir2hll.h +++ b/include/retdec/llvmir2hll/llvmir2hll.h @@ -73,7 +73,6 @@ #include "retdec/llvmir2hll/var_name_gen/var_name_gens/num_var_name_gen.h" #include "retdec/llvmir2hll/var_renamer/var_renamer.h" #include "retdec/llvmir2hll/var_renamer/var_renamer_factory.h" -#include "retdec/llvm-support/diagnostics.h" #include "retdec/utils/container.h" #include "retdec/utils/conversion.h" #include "retdec/utils/memory.h" diff --git a/include/retdec/llvmir2hll/optimizer/optimizer_manager.h b/include/retdec/llvmir2hll/optimizer/optimizer_manager.h index 21f675279..cd694fcc5 100644 --- a/include/retdec/llvmir2hll/optimizer/optimizer_manager.h +++ b/include/retdec/llvmir2hll/optimizer/optimizer_manager.h @@ -10,7 +10,6 @@ #include "retdec/llvmir2hll/optimizer/optimizer.h" #include "retdec/llvmir2hll/support/smart_ptr.h" #include "retdec/llvmir2hll/support/types.h" -#include "retdec/llvm-support/diagnostics.h" #include "retdec/utils/non_copyable.h" namespace retdec { diff --git a/include/retdec/llvmir2hll/pattern/pattern_finder_runners/cli_pattern_finder_runner.h b/include/retdec/llvmir2hll/pattern/pattern_finder_runners/cli_pattern_finder_runner.h index 462347de8..6868a34ec 100644 --- a/include/retdec/llvmir2hll/pattern/pattern_finder_runners/cli_pattern_finder_runner.h +++ b/include/retdec/llvmir2hll/pattern/pattern_finder_runners/cli_pattern_finder_runner.h @@ -10,6 +10,7 @@ #include #include "retdec/llvmir2hll/pattern/pattern_finder_runner.h" +#include "retdec/utils/io/log.h" namespace retdec { namespace llvmir2hll { @@ -23,7 +24,7 @@ namespace llvmir2hll { */ class CLIPatternFinderRunner: public PatternFinderRunner { public: - CLIPatternFinderRunner(llvm::raw_ostream &os); + CLIPatternFinderRunner(utils::io::Logger &os); private: virtual void doActionsBeforePatternFinderRuns(ShPtr pf) override; @@ -34,7 +35,7 @@ class CLIPatternFinderRunner: public PatternFinderRunner { private: /// Output stream, into which the patterns will be emitted. - llvm::raw_ostream &os; + utils::io::Logger &os; }; } // namespace llvmir2hll diff --git a/include/retdec/llvmir2hll/validator/validator.h b/include/retdec/llvmir2hll/validator/validator.h index 9a840df8c..213d77d93 100644 --- a/include/retdec/llvmir2hll/validator/validator.h +++ b/include/retdec/llvmir2hll/validator/validator.h @@ -11,7 +11,9 @@ #include "retdec/llvmir2hll/support/smart_ptr.h" #include "retdec/llvmir2hll/support/visitors/ordered_all_visitor.h" -#include "retdec/llvm-support/diagnostics.h" +#include "retdec/utils/io/log.h" + +using namespace retdec::utils::io; namespace retdec { namespace llvmir2hll { @@ -45,12 +47,10 @@ class Validator: protected OrderedAllVisitor { /** * @brief Function to be called when there is a validation error. */ - template - void validationError(const std::string &warningMessage, Args &&... args) { + void validationError(const std::string &warningMessage) { moduleIsCorrect = false; if (printMessageOnError) { - retdec::llvm_support::printWarningMessage(warningMessage, - std::forward(args)...); + Log::error() << Log::Warning << warningMessage << std::endl; } } diff --git a/include/retdec/unpacker/plugin.h b/include/retdec/unpacker/plugin.h index 23ff58d19..010d6b1a2 100644 --- a/include/retdec/unpacker/plugin.h +++ b/include/retdec/unpacker/plugin.h @@ -7,15 +7,17 @@ #ifndef RETDEC_UNPACKER_PLUGIN_H #define RETDEC_UNPACKER_PLUGIN_H -#include #include #include #include +#include "retdec/utils/io/log.h" #include "retdec/unpacker/unpacker_exception.h" #define plugin(T) retdec::unpackertool::Plugin::instance() +using namespace retdec::utils::io; + namespace retdec { namespace unpackertool { @@ -171,7 +173,7 @@ class Plugin */ template void log(const Args&... args) { - Plugin::logImpl(std::cout, "[", getInfo()->name, "] ", args...); + Plugin::logImpl(Log::get(Log::Type::Info), "[", getInfo()->name, "] ", args...); } /** @@ -184,7 +186,7 @@ class Plugin */ template void error(const Args&... args) { - Plugin::logImpl(std::cerr, "[ERROR] [", getInfo()->name, "] ", args...); + Plugin::logImpl(Log::get(Log::Type::Error), "[ERROR] [", getInfo()->name, "] ", args...); } /** @@ -211,13 +213,13 @@ class Plugin private: PluginExitCode _cachedExitCode; ///< Cached exit code of the plugin for the unpacked file. - template static void logImpl(std::ostream& out, const T& data, const Args&... args) + template static void logImpl(Logger& out, const T& data, const Args&... args) { out << data; logImpl(out, args...); } - static void logImpl(std::ostream& out) + static void logImpl(Logger& out) { out << std::endl; } diff --git a/include/retdec/utils/io/log.h b/include/retdec/utils/io/log.h new file mode 100644 index 000000000..84cfe2ed5 --- /dev/null +++ b/include/retdec/utils/io/log.h @@ -0,0 +1,142 @@ +/** +* @file include/retdec/utils/io/logger.h +* @brief Provides unified logging interface. +* @copyright (c) 2020 Avast Software, licensed under the MIT license +*/ + +#ifndef RETDEC_UTILS_IO_LOG_H +#define RETDEC_UTILS_IO_LOG_H + +#include "retdec/utils/io/logger.h" + +namespace retdec { +namespace utils { +namespace io { + +class Log { +public: + /** + * Each type represents different logging style. For each + * type is provided a logger by calling Log::get function + */ + enum class Type : int { + Info = 0, + Debug, + Error, + Undefined + }; + + using Color = Logger::Color; + using Action = Logger::Action; + +public: + /** + * Returns corresponding initialized logger for logType provided + * as parameter. At the beginning all the logger types are initialized + * to default logger. + * + * For debug/info: + * - verbose + * - logs to std::out + * + * For error: + * - verbose + * - logs to std::err + */ + static Logger& get(const Type& logType); + + /** + * Sets appropriate logger based on logType value. + */ + static void set(const Type& logType, Logger::Ptr&& logger); + + /** + * Shortcut for Logger(Log::get(Log::Type::Info)). + * + * Creates temporary copy of Info logger. This is particularly + * useful when changing color of the output. On destruction + * color is changed to default. + */ + static Logger info(); + + /** + * Shortcut for Logger(Log::get(Log::Type::Debug)). + * + * Creates temporary copy of Debug logger. This is particularly + * useful when changing color of the output. On destruction + * color is changed to default. + */ + static Logger debug(); + + /** + * Shortcut for Logger(Log::get(Log::Type::Error)). + * + * Creates temporary copy of Error logger. This is particularly + * useful when changing color of the output. On destruction + * color is changed to default. + */ + static Logger error(); + + /** + * Shortcut for Log::info() << action << phaseId << Log::Action::ElapsedTime << std::endl. + */ + static void phase( + const std::string& phaseId, + const Log::Action& action = Log::Action::Phase); + +public: + /** + * Representation of Error Action that can be inserted into logger. + * Shortcut for Log::Action::Error + */ + static const Action Error; + + /** + * Representation of Warning Action that can be inserted into logger. + * Shortcut for Log::Action::Warning + */ + static const Action Warning; + + /** + * Representation of Phase Action that can be inserted into logger. + * Shortcut for Log::Action::Phase + */ + static const Action Phase; + + /** + * Representation of SubPhase Action that can be inserted into logger. + * Shortcut for Log::Action::SubPhase + */ + static const Action SubPhase; + + /** + * Representation of SubSubPhase Action that can be inserted into logger. + * Shortcut for Log::Action::SubSubPhase + */ + static const Action SubSubPhase; + + /** + * Representation of ElapsedTime Action that can be inserted into logger. + * Shortcut for Log::Action::ElapsedTime + */ + static const Action ElapsedTime; + +private: + /** + * Structure containing initialized/default loggers. + */ + static Logger::Ptr writers[static_cast(Type::Undefined)+1]; + + /** + * Fallback logger. In case of bad initialization of the writers + * this logger is used as fallback to log (calling set with nullptr). + */ + static Logger defaultLogger; +}; + +} +} +} + + +#endif diff --git a/include/retdec/utils/io/logger.h b/include/retdec/utils/io/logger.h new file mode 100644 index 000000000..9fa8837be --- /dev/null +++ b/include/retdec/utils/io/logger.h @@ -0,0 +1,107 @@ +/** +* @file include/retdec/utils/io/logger.h +* @brief Implementation of a logging class. +* @copyright (c) 2020 Avast Software, licensed under the MIT license +*/ + +#ifndef RETDEC_UTILS_IO_LOGGER_H +#define RETDEC_UTILS_IO_LOGGER_H + +#include +#include +#include +#include + +namespace retdec { +namespace utils { +namespace io { + +/** + * @brief Provides Logger inteface that is used for logging events during decompilation. + */ +class Logger { +public: + using Ptr = std::unique_ptr; + +public: + enum Action: int { + Phase, + SubPhase, + SubSubPhase, + ElapsedTime, + Error, + Warning, + NoAction + }; + + enum class Color: int { + Red, + Green, + Blue, + Yellow, + DarkCyan, + Default + }; + +protected: + typedef std::ostream& (*StreamManipulator) (std::ostream&); + +public: + Logger(std::ostream& stream, bool verbose = true); + Logger(const Logger& logger); + ~Logger(); + + template + Logger& operator << (const T& p); + + Logger& operator << (const StreamManipulator& manip); + Logger& operator << (const Action& ia); + Logger& operator << (const Color& lc); + +private: + bool isRedirected(const std::ostream& stream) const; + +protected: + std::ostream& _out; + + bool _verbose = true; + Color _currentBrush = Color::Default; + + bool _modifiedTerminalProperty = false; + bool _terminalNotSupported = false; +}; + +class FileLogger : public Logger { +public: + FileLogger(const std::string& file, bool verbose = true); + +private: + std::ofstream _file; +}; + +template +inline Logger& Logger::operator << (const T& p) +{ + if (!_verbose) + return *this; + + _out << p; + + return *this; +} + +inline Logger& Logger::operator << (const Logger::StreamManipulator& p) +{ + if (!_verbose) + return *this; + + _out << p; + + return *this; +} + +} +} +} + +#endif diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index e3635ae12..9897541c5 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -16,7 +16,6 @@ cond_add_subdirectory(fileformat RETDEC_ENABLE_FILEFORMAT) cond_add_subdirectory(fileinfo RETDEC_ENABLE_FILEINFO) cond_add_subdirectory(getsig RETDEC_ENABLE_GETSIG) cond_add_subdirectory(idr2pat RETDEC_ENABLE_IDR2PAT) -cond_add_subdirectory(llvm-support RETDEC_ENABLE_LLVM_SUPPORT) cond_add_subdirectory(llvmir-emul RETDEC_ENABLE_LLVMIR_EMUL) cond_add_subdirectory(llvmir2hll RETDEC_ENABLE_LLVMIR2HLL) cond_add_subdirectory(loader RETDEC_ENABLE_LOADER) diff --git a/src/ar-extractortool/ar_extractor.cpp b/src/ar-extractortool/ar_extractor.cpp index 7e919d990..d446505f6 100644 --- a/src/ar-extractortool/ar_extractor.cpp +++ b/src/ar-extractortool/ar_extractor.cpp @@ -4,7 +4,6 @@ * @copyright (c) 2017 Avast Software, licensed under the MIT license */ -#include #include #include @@ -12,10 +11,12 @@ #include #include "retdec/utils/filesystem.h" +#include "retdec/utils/io/log.h" #include "retdec/ar-extractor/archive_wrapper.h" #include "retdec/ar-extractor/detection.h" using namespace retdec::utils; +using namespace retdec::utils::io; using namespace retdec::ar_extractor; using namespace rapidjson; @@ -43,13 +44,12 @@ bool isJson = false; /** * Print usage. * - * @param outputStream target stream + * @param log usage logger object */ -void printUsage( - std::ostream &outputStream) +void printUsage(Logger& log) { - outputStream << "Usage: ar_extractor [OPTIONS] FILE\n\n" + log << "Usage: ar_extractor [OPTIONS] FILE\n\n" "Options:\n\n" "--arch-magic\n" " Check if file starts with archive magic constants.\n" @@ -99,7 +99,7 @@ void printUsage( void printErrorPlainText( const std::string &message) { - std::cerr << "Error: " << message << ".\n"; + Log::error() << Log::Error << message << ".\n"; } /** @@ -120,7 +120,7 @@ void printErrorJson( PrettyWriter writer(buffer); errorFile.Accept(writer); - std::cerr << buffer.GetString() << "\n"; + Log::error() << buffer.GetString() << "\n"; } /** @@ -163,7 +163,7 @@ int printTable( } } - std::cout << result; + Log::info() << result; return 0; } @@ -213,7 +213,7 @@ int processArguments( const auto &arg = args[i]; if (arg == "-h" || arg == "--help") { - printUsage(std::cout); + printUsage(Log::get(Log::Type::Info)); return 0; } else if (arg == "-o" || arg == "--output") { @@ -339,7 +339,7 @@ int processArguments( return printTable(archive, fixNames, isNum); case ACTION::OBJECT_COUNT: - std::cout << archive.getNumberOfObjects() << "\n"; + Log::info() << archive.getNumberOfObjects() << "\n"; return 0; case ACTION::EXTRACT_NAME: diff --git a/src/bin2llvmir/CMakeLists.txt b/src/bin2llvmir/CMakeLists.txt index a941377e7..de1e63317 100644 --- a/src/bin2llvmir/CMakeLists.txt +++ b/src/bin2llvmir/CMakeLists.txt @@ -136,7 +136,6 @@ target_link_libraries(bin2llvmir retdec::ctypesparser retdec::common retdec::utils - retdec::llvm-support retdec::deps::llvm ) diff --git a/src/bin2llvmir/analyses/ctor_dtor.cpp b/src/bin2llvmir/analyses/ctor_dtor.cpp index ebf1d7d77..013fcb73c 100644 --- a/src/bin2llvmir/analyses/ctor_dtor.cpp +++ b/src/bin2llvmir/analyses/ctor_dtor.cpp @@ -4,8 +4,6 @@ * @copyright (c) 2017 Avast Software, licensed under the MIT license */ -#include - #include #include #include diff --git a/src/bin2llvmir/analyses/reaching_definitions.cpp b/src/bin2llvmir/analyses/reaching_definitions.cpp index a3c3c8ba4..6c791a7ec 100644 --- a/src/bin2llvmir/analyses/reaching_definitions.cpp +++ b/src/bin2llvmir/analyses/reaching_definitions.cpp @@ -5,7 +5,6 @@ */ #include -#include #include #include #include diff --git a/src/bin2llvmir/analyses/symbolic_tree.cpp b/src/bin2llvmir/analyses/symbolic_tree.cpp index 9207ad256..cdc61d624 100644 --- a/src/bin2llvmir/analyses/symbolic_tree.cpp +++ b/src/bin2llvmir/analyses/symbolic_tree.cpp @@ -4,7 +4,6 @@ * @copyright (c) 2017 Avast Software, licensed under the MIT license */ -#include #include #include diff --git a/src/bin2llvmir/optimizations/asm_inst_remover/asm_inst_remover.cpp b/src/bin2llvmir/optimizations/asm_inst_remover/asm_inst_remover.cpp index 5f260235e..a657f970e 100644 --- a/src/bin2llvmir/optimizations/asm_inst_remover/asm_inst_remover.cpp +++ b/src/bin2llvmir/optimizations/asm_inst_remover/asm_inst_remover.cpp @@ -5,8 +5,6 @@ * @copyright (c) 2017 Avast Software, licensed under the MIT license */ -#include - #include "retdec/bin2llvmir/optimizations/asm_inst_remover/asm_inst_remover.h" #include "retdec/bin2llvmir/providers/asm_instruction.h" #include "retdec/bin2llvmir/providers/names.h" diff --git a/src/bin2llvmir/optimizations/class_hierarchy/hierarchy_analysis.cpp b/src/bin2llvmir/optimizations/class_hierarchy/hierarchy_analysis.cpp index 00dc1a768..fba0fc948 100644 --- a/src/bin2llvmir/optimizations/class_hierarchy/hierarchy_analysis.cpp +++ b/src/bin2llvmir/optimizations/class_hierarchy/hierarchy_analysis.cpp @@ -4,8 +4,6 @@ * @copyright (c) 2017 Avast Software, licensed under the MIT license */ -#include - #include "retdec/bin2llvmir/utils/llvm.h" #include "retdec/bin2llvmir/optimizations/class_hierarchy/hierarchy_analysis.h" #include "retdec/bin2llvmir/utils/debug.h" diff --git a/src/bin2llvmir/optimizations/constants/constants.cpp b/src/bin2llvmir/optimizations/constants/constants.cpp index 22b3aeb7b..38ba873de 100644 --- a/src/bin2llvmir/optimizations/constants/constants.cpp +++ b/src/bin2llvmir/optimizations/constants/constants.cpp @@ -6,7 +6,6 @@ #include #include -#include #include #include #include diff --git a/src/bin2llvmir/optimizations/decoder/decoder.cpp b/src/bin2llvmir/optimizations/decoder/decoder.cpp index f8e87cee9..5015b634a 100644 --- a/src/bin2llvmir/optimizations/decoder/decoder.cpp +++ b/src/bin2llvmir/optimizations/decoder/decoder.cpp @@ -9,6 +9,7 @@ #include "retdec/utils/conversion.h" #include "retdec/utils/string.h" +#include "retdec/utils/io/log.h" #include "retdec/bin2llvmir/optimizations/decoder/decoder.h" #include "retdec/bin2llvmir/utils/llvm.h" #include "retdec/bin2llvmir/utils/capstone.h" @@ -17,6 +18,7 @@ using namespace retdec::capstone2llvmir; using namespace retdec::common; using namespace retdec::bin2llvmir::st_match; using namespace retdec::fileformat; +using namespace retdec::utils::io; namespace retdec { namespace bin2llvmir { @@ -88,7 +90,7 @@ bool Decoder::runCatcher() } catch (const BaseError& e) { - std::cerr << "[capstone2llvmir]: " << e.what() << std::endl; + Log::error() << "[capstone2llvmir]: " << e.what() << std::endl; exit(1); } } diff --git a/src/bin2llvmir/optimizations/idioms/idioms.cpp b/src/bin2llvmir/optimizations/idioms/idioms.cpp index 502802dd5..58056c4f4 100644 --- a/src/bin2llvmir/optimizations/idioms/idioms.cpp +++ b/src/bin2llvmir/optimizations/idioms/idioms.cpp @@ -4,8 +4,6 @@ * @copyright (c) 2017 Avast Software, licensed under the MIT license */ -#include - #include "retdec/bin2llvmir/optimizations/idioms/idioms.h" #include "retdec/bin2llvmir/optimizations/idioms/idioms_borland.h" #include "retdec/bin2llvmir/optimizations/idioms/idioms_common.h" diff --git a/src/bin2llvmir/optimizations/idioms/idioms_magicdivmod.cpp b/src/bin2llvmir/optimizations/idioms/idioms_magicdivmod.cpp index c899aa050..65009ca29 100644 --- a/src/bin2llvmir/optimizations/idioms/idioms_magicdivmod.cpp +++ b/src/bin2llvmir/optimizations/idioms/idioms_magicdivmod.cpp @@ -4,8 +4,6 @@ * @copyright (c) 2017 Avast Software, licensed under the MIT license */ -#include - #include #include "retdec/bin2llvmir/optimizations/idioms/idioms_magicdivmod.h" diff --git a/src/bin2llvmir/optimizations/idioms_libgcc/idioms_libgcc.cpp b/src/bin2llvmir/optimizations/idioms_libgcc/idioms_libgcc.cpp index 9369d2b95..ee1ece014 100644 --- a/src/bin2llvmir/optimizations/idioms_libgcc/idioms_libgcc.cpp +++ b/src/bin2llvmir/optimizations/idioms_libgcc/idioms_libgcc.cpp @@ -4,7 +4,6 @@ * @copyright (c) 2017 Avast Software, licensed under the MIT license */ -#include #include #include diff --git a/src/bin2llvmir/optimizations/param_return/param_return.cpp b/src/bin2llvmir/optimizations/param_return/param_return.cpp index 44ada88cf..e40da74f2 100644 --- a/src/bin2llvmir/optimizations/param_return/param_return.cpp +++ b/src/bin2llvmir/optimizations/param_return/param_return.cpp @@ -6,7 +6,6 @@ #include #include -#include #include #include diff --git a/src/bin2llvmir/optimizations/provider_init/provider_init.cpp b/src/bin2llvmir/optimizations/provider_init/provider_init.cpp index 17fc0a64c..1a4bb5c1a 100644 --- a/src/bin2llvmir/optimizations/provider_init/provider_init.cpp +++ b/src/bin2llvmir/optimizations/provider_init/provider_init.cpp @@ -4,11 +4,11 @@ * @copyright (c) 2017 Avast Software, licensed under the MIT license */ -#include #include #include +#include "retdec/utils/io/log.h" #include "retdec/bin2llvmir/analyses/symbolic_tree.h" #include "retdec/bin2llvmir/optimizations/provider_init/provider_init.h" #include "retdec/bin2llvmir/providers/abi/abi.h" @@ -25,6 +25,7 @@ #include "retdec/yaracpp/yara_detector.h" using namespace llvm; +using namespace retdec::utils::io; namespace retdec { namespace bin2llvmir { @@ -340,7 +341,7 @@ bool ProviderInitialization::runOnModule(Module& m) { if (l.bytecode) { - std::cerr << "Warning: Detected " << l.name + Log::error() << Log::Warning << "Detected " << l.name << " bytecode, which cannot be decompiled by our " "machine-code decompiler. " "The decompilation result may be inaccurate."; diff --git a/src/bin2llvmir/optimizations/select_functions/select_functions.cpp b/src/bin2llvmir/optimizations/select_functions/select_functions.cpp index b5572bab6..80d54f81b 100644 --- a/src/bin2llvmir/optimizations/select_functions/select_functions.cpp +++ b/src/bin2llvmir/optimizations/select_functions/select_functions.cpp @@ -6,7 +6,6 @@ */ #include -#include #include #include diff --git a/src/bin2llvmir/optimizations/simple_types/simple_types.cpp b/src/bin2llvmir/optimizations/simple_types/simple_types.cpp index 80bd72fc8..e8a7a5854 100644 --- a/src/bin2llvmir/optimizations/simple_types/simple_types.cpp +++ b/src/bin2llvmir/optimizations/simple_types/simple_types.cpp @@ -5,7 +5,6 @@ */ #include -#include #include #include #include diff --git a/src/bin2llvmir/optimizations/stack_pointer_ops/stack_pointer_ops.cpp b/src/bin2llvmir/optimizations/stack_pointer_ops/stack_pointer_ops.cpp index 46cc29111..bcbdc699d 100644 --- a/src/bin2llvmir/optimizations/stack_pointer_ops/stack_pointer_ops.cpp +++ b/src/bin2llvmir/optimizations/stack_pointer_ops/stack_pointer_ops.cpp @@ -6,7 +6,6 @@ #include #include -#include #include #include diff --git a/src/bin2llvmir/optimizations/syscalls/arm.cpp b/src/bin2llvmir/optimizations/syscalls/arm.cpp index 38f018cca..486a6be57 100644 --- a/src/bin2llvmir/optimizations/syscalls/arm.cpp +++ b/src/bin2llvmir/optimizations/syscalls/arm.cpp @@ -4,8 +4,6 @@ * @copyright (c) 2017 Avast Software, licensed under the MIT license */ -#include - #include #include "retdec/bin2llvmir/optimizations/syscalls/syscalls.h" diff --git a/src/bin2llvmir/optimizations/syscalls/mips.cpp b/src/bin2llvmir/optimizations/syscalls/mips.cpp index 232d47fd8..4cbf76f56 100644 --- a/src/bin2llvmir/optimizations/syscalls/mips.cpp +++ b/src/bin2llvmir/optimizations/syscalls/mips.cpp @@ -4,8 +4,6 @@ * @copyright (c) 2017 Avast Software, licensed under the MIT license */ -#include - #include #include "retdec/bin2llvmir/optimizations/syscalls/syscalls.h" diff --git a/src/bin2llvmir/optimizations/syscalls/x86.cpp b/src/bin2llvmir/optimizations/syscalls/x86.cpp index e6eb7c3cd..6527818e6 100644 --- a/src/bin2llvmir/optimizations/syscalls/x86.cpp +++ b/src/bin2llvmir/optimizations/syscalls/x86.cpp @@ -4,8 +4,6 @@ * @copyright (c) 2017 Avast Software, licensed under the MIT license */ -#include - #include #include "retdec/bin2llvmir/optimizations/syscalls/syscalls.h" diff --git a/src/bin2llvmir/optimizations/types_propagator/types_propagator.cpp b/src/bin2llvmir/optimizations/types_propagator/types_propagator.cpp index dff20fdb7..7d32ceaf1 100644 --- a/src/bin2llvmir/optimizations/types_propagator/types_propagator.cpp +++ b/src/bin2llvmir/optimizations/types_propagator/types_propagator.cpp @@ -4,12 +4,14 @@ * @copyright (c) 2020 Avast Software, licensed under the MIT license */ -#include #include #include +#include "retdec/utils/io/log.h" #include "retdec/bin2llvmir/optimizations/types_propagator/types_propagator.h" +using namespace retdec::utils::io; + namespace retdec { namespace bin2llvmir { @@ -59,13 +61,13 @@ bool TypesPropagator::run() unsigned cntr = 0; for (auto& eq : _eqSets) { - std::cout << "set #" << cntr++ << std::endl; + Log::info() << "set #" << cntr++ << std::endl; for (auto& v : eq) { if (llvm::isa(v)) - std::cout << "\t\t" << v->getName().str() << std::endl; + Log::info() << "\t\t" << v->getName().str() << std::endl; else - std::cout << "\t\t" << llvmObjToString(v) << std::endl; + Log::info() << "\t\t" << llvmObjToString(v) << std::endl; } } exit(1); diff --git a/src/bin2llvmir/optimizations/unreachable_funcs/unreachable_funcs.cpp b/src/bin2llvmir/optimizations/unreachable_funcs/unreachable_funcs.cpp index f660202c8..14c39e2e9 100644 --- a/src/bin2llvmir/optimizations/unreachable_funcs/unreachable_funcs.cpp +++ b/src/bin2llvmir/optimizations/unreachable_funcs/unreachable_funcs.cpp @@ -4,7 +4,6 @@ * @copyright (c) 2017 Avast Software, licensed under the MIT license */ -#include #include #include diff --git a/src/bin2llvmir/optimizations/writer_dsm/writer_dsm.cpp b/src/bin2llvmir/optimizations/writer_dsm/writer_dsm.cpp index b1258e0d7..191bf8623 100644 --- a/src/bin2llvmir/optimizations/writer_dsm/writer_dsm.cpp +++ b/src/bin2llvmir/optimizations/writer_dsm/writer_dsm.cpp @@ -8,7 +8,6 @@ #include #include #include -#include #include #include diff --git a/src/bin2llvmir/optimizations/x87_fpu/x87_fpu.cpp b/src/bin2llvmir/optimizations/x87_fpu/x87_fpu.cpp index bb05adda0..eaac8f07e 100644 --- a/src/bin2llvmir/optimizations/x87_fpu/x87_fpu.cpp +++ b/src/bin2llvmir/optimizations/x87_fpu/x87_fpu.cpp @@ -11,6 +11,7 @@ #include #include +#include "retdec/utils/io/log.h" #include "retdec/bin2llvmir/optimizations/x87_fpu/x87_fpu.h" #include "retdec/utils/string.h" #include "retdec/bin2llvmir/providers/asm_instruction.h" @@ -23,6 +24,7 @@ using namespace llvm; using namespace retdec::bin2llvmir::llvm_utils; +using namespace retdec::utils::io; namespace retdec { namespace bin2llvmir { diff --git a/src/bin2llvmir/providers/config.cpp b/src/bin2llvmir/providers/config.cpp index e9de98baf..650e2d055 100644 --- a/src/bin2llvmir/providers/config.cpp +++ b/src/bin2llvmir/providers/config.cpp @@ -4,8 +4,6 @@ * @copyright (c) 2017 Avast Software, licensed under the MIT license */ -#include - #include #include diff --git a/src/bin2llvmir/providers/lti.cpp b/src/bin2llvmir/providers/lti.cpp index e0b23019b..0246811df 100644 --- a/src/bin2llvmir/providers/lti.cpp +++ b/src/bin2llvmir/providers/lti.cpp @@ -5,7 +5,6 @@ */ #include -#include #include "retdec/ctypes/floating_point_type.h" #include "retdec/ctypes/function_type.h" diff --git a/src/bin2llvmir/retdec-bin2llvmir-config.cmake b/src/bin2llvmir/retdec-bin2llvmir-config.cmake index 7403e795d..99037aab9 100644 --- a/src/bin2llvmir/retdec-bin2llvmir-config.cmake +++ b/src/bin2llvmir/retdec-bin2llvmir-config.cmake @@ -15,7 +15,6 @@ if(NOT TARGET retdec::bin2llvmir) ctypesparser common utils - llvm-support llvm ) diff --git a/src/bin2llvmir/utils/ir_modifier.cpp b/src/bin2llvmir/utils/ir_modifier.cpp index fc1b7c54d..b44fc1b98 100644 --- a/src/bin2llvmir/utils/ir_modifier.cpp +++ b/src/bin2llvmir/utils/ir_modifier.cpp @@ -4,8 +4,6 @@ * @copyright (c) 2017 Avast Software, licensed under the MIT license */ -#include - #include #include "retdec/utils/string.h" diff --git a/src/bin2pat/bin2pat.cpp b/src/bin2pat/bin2pat.cpp index bd8745393..f26669bf2 100644 --- a/src/bin2pat/bin2pat.cpp +++ b/src/bin2pat/bin2pat.cpp @@ -6,11 +6,11 @@ #include #include -#include #include #include #include "retdec/utils/filesystem.h" +#include "retdec/utils/io/log.h" #include "retdec/patterngen/pattern_extractor/pattern_extractor.h" #include "yaramod/yaramod.h" @@ -21,12 +21,12 @@ * Output is set of yara rules (http://yara.readthedocs.io/en/v3.5.0/). */ +using namespace retdec::utils::io; using namespace retdec::patterngen; -void printUsage( - std::ostream &outputStream) +void printUsage(Logger &log) { - outputStream << "Usage: bin2pat [-o OUTPUT_FILE] [-n NOTE]" + log << "Usage: bin2pat [-o OUTPUT_FILE] [-n NOTE]" << " \n\n" << "-o --output OUTPUT_FILE\n" << " Output file path (if not given, stdout is used).\n" @@ -42,8 +42,8 @@ void printUsage( void printErrorAndDie( const std::string &message) { - std::cerr << "Error: " << message << ".\n"; - printUsage(std::cerr); + Log::error() << Log::Error << message << ".\n"; + printUsage(Log::get(Log::Type::Error)); std::exit(1); } @@ -62,7 +62,7 @@ void processArgs( for (std::size_t i = 0, e = args.size(); i < e; ++i) { if (args[i] == "--help" || args[i] == "-h") { - printUsage(std::cout); + printUsage(Log::get(Log::Type::Info)); return; } else if (args[i] == "-o" || args[i] == "--output") { @@ -143,8 +143,8 @@ void processArgs( if (!extractor.isValid()) { // Sometimes, non-supported files are present in archives. We will // only print warning if such a file is encountered. - std::cerr << "Error: file '" << path << "' was not processed.\n"; - std::cerr << "Problem: " << extractor.getErrorMessage() << ".\n\n"; + Log::error() << Log::Error << "file '" << path << "' was not processed.\n"; + Log::error() << "Problem: " << extractor.getErrorMessage() << ".\n\n"; continue; } else { @@ -154,11 +154,11 @@ void processArgs( // Print warnings if any. const auto &warnings = extractor.getWarnings(); if (!warnings.empty()) { - std::cerr << "Warning: problems with file '" << path << "'\n"; + Log::error() << Log::Warning << "problems with file '" << path << "'\n"; for (const auto &warning : warnings) { - std::cerr << "Problem: " << warning << ".\n"; + Log::error() << "Problem: " << warning << ".\n"; } - std::cerr << "\n"; + Log::error() << "\n"; } } } @@ -179,7 +179,7 @@ void processArgs( outputFile << builder.get(false)->getText() << "\n"; } else { - std::cout << builder.get(false)->getText() << "\n"; + Log::info() << builder.get(false)->getText() << "\n"; } } diff --git a/src/capstone2llvmir/arm/arm.cpp b/src/capstone2llvmir/arm/arm.cpp index aa9b3edbd..b18c90421 100644 --- a/src/capstone2llvmir/arm/arm.cpp +++ b/src/capstone2llvmir/arm/arm.cpp @@ -5,7 +5,6 @@ */ #include -#include #include "capstone2llvmir/arm/arm_impl.h" diff --git a/src/capstone2llvmir/arm64/arm64.cpp b/src/capstone2llvmir/arm64/arm64.cpp index 30a5ee782..a5e7c7e8d 100644 --- a/src/capstone2llvmir/arm64/arm64.cpp +++ b/src/capstone2llvmir/arm64/arm64.cpp @@ -5,10 +5,13 @@ */ #include -#include + +#include "retdec/utils/io/log.h" #include "capstone2llvmir/arm64/arm64_impl.h" +using namespace retdec::utils::io; + namespace retdec { namespace capstone2llvmir { @@ -704,7 +707,7 @@ llvm::Value* Capstone2LlvmIrTranslatorArm64_impl::loadRegister( // If we dont find the register type, try to recover from this returning at // least the number of register // Maybe solve this better - std::cerr << e.what() << std::endl; + Log::error() << e.what() << std::endl; return llvm::ConstantInt::get(dstType ? dstType : getDefaultType(), r); } @@ -818,7 +821,7 @@ llvm::Instruction* Capstone2LlvmIrTranslatorArm64_impl::storeRegister( if (llvmReg == nullptr) { // Maybe return xchg eax, eax? - std::cerr << "storeRegister() unhandled reg." << std::endl; + Log::error() << "storeRegister() unhandled reg." << std::endl; return nullptr; } diff --git a/src/capstone2llvmir/capstone2llvmir_impl.cpp b/src/capstone2llvmir/capstone2llvmir_impl.cpp index 3f10248ba..20231525d 100644 --- a/src/capstone2llvmir/capstone2llvmir_impl.cpp +++ b/src/capstone2llvmir/capstone2llvmir_impl.cpp @@ -6,7 +6,6 @@ */ #include -#include #include "capstone2llvmir/capstone2llvmir_impl.h" diff --git a/src/capstone2llvmir/llvmir_utils.cpp b/src/capstone2llvmir/llvmir_utils.cpp index 99769bb6a..d35ffecfe 100644 --- a/src/capstone2llvmir/llvmir_utils.cpp +++ b/src/capstone2llvmir/llvmir_utils.cpp @@ -4,8 +4,6 @@ * @copyright (c) 2018 Avast Software, licensed under the MIT license */ -#include - #include "capstone2llvmir/llvmir_utils.h" #include "retdec/capstone2llvmir/exceptions.h" diff --git a/src/capstone2llvmir/mips/mips.cpp b/src/capstone2llvmir/mips/mips.cpp index 695aa8b8f..bd16706a1 100644 --- a/src/capstone2llvmir/mips/mips.cpp +++ b/src/capstone2llvmir/mips/mips.cpp @@ -5,7 +5,6 @@ */ #include -#include #include "capstone2llvmir/mips/mips_impl.h" diff --git a/src/capstone2llvmir/powerpc/powerpc.cpp b/src/capstone2llvmir/powerpc/powerpc.cpp index 9696a055d..886bc3283 100644 --- a/src/capstone2llvmir/powerpc/powerpc.cpp +++ b/src/capstone2llvmir/powerpc/powerpc.cpp @@ -5,7 +5,6 @@ */ #include -#include #include "capstone2llvmir/powerpc/powerpc_impl.h" diff --git a/src/capstone2llvmir/x86/x86.cpp b/src/capstone2llvmir/x86/x86.cpp index ced4fc3ff..83717eb28 100644 --- a/src/capstone2llvmir/x86/x86.cpp +++ b/src/capstone2llvmir/x86/x86.cpp @@ -5,7 +5,6 @@ */ #include -#include #include "capstone2llvmir/x86/x86_impl.h" diff --git a/src/capstone2llvmirtool/capstone2llvmir.cpp b/src/capstone2llvmirtool/capstone2llvmir.cpp index e59020bab..0f1dbe837 100644 --- a/src/capstone2llvmirtool/capstone2llvmir.cpp +++ b/src/capstone2llvmirtool/capstone2llvmir.cpp @@ -5,7 +5,6 @@ */ #include -#include #include #include @@ -15,9 +14,12 @@ #include "retdec/common/address.h" #include "retdec/utils/conversion.h" #include "retdec/utils/string.h" +#include "retdec/utils/io/log.h" #include "retdec/capstone2llvmir/capstone2llvmir.h" +using namespace retdec::utils::io; + // byte ptr [0x12345678], 0x11 std::vector CODE = retdec::utils::hexStringToBytes("80 05 78 56 34 12 11 00"); @@ -143,7 +145,7 @@ class ProgramOptions case CS_ARCH_MAX: case CS_ARCH_ALL: default: - std::cerr << "Can not get Capstone arch to default Capstone basic mode." << std::endl; + Log::error() << "Can not get Capstone arch to default Capstone basic mode." << std::endl; exit(1); } } @@ -152,23 +154,23 @@ class ProgramOptions { std::string tmp; retdec::utils::bytesToHexString(code, tmp, 0, 0, false, true); - std::cout << std::endl; - std::cout << "Program Options:" << std::endl; - std::cout << "\t" << "arch : " << arch << " (" << _arch << ")" << std::endl; - std::cout << "\t" << "base : " << std::hex << base << " (" << _base << ")" << std::endl; - std::cout << "\t" << "code : " << tmp << " (" << _code << ")" << std::endl; - std::cout << "\t" << "asm text : " << text << std::endl; - std::cout << "\t" << "b mode : " << std::hex << basicMode << " (" << _basicMode << ")" << std::endl; - std::cout << "\t" << "e mode : " << std::hex << extraMode << " (" << _extraMode << ")" << std::endl; - std::cout << "\t" << "out : " << outFile << std::endl; - std::cout << std::endl; + Log::info() << std::endl; + Log::info() << "Program Options:" << std::endl; + Log::info() << "\t" << "arch : " << arch << " (" << _arch << ")" << std::endl; + Log::info() << "\t" << "base : " << std::hex << base << " (" << _base << ")" << std::endl; + Log::info() << "\t" << "code : " << tmp << " (" << _code << ")" << std::endl; + Log::info() << "\t" << "asm text : " << text << std::endl; + Log::info() << "\t" << "b mode : " << std::hex << basicMode << " (" << _basicMode << ")" << std::endl; + Log::info() << "\t" << "e mode : " << std::hex << extraMode << " (" << _extraMode << ")" << std::endl; + Log::info() << "\t" << "out : " << outFile << std::endl; + Log::info() << std::endl; } void printHelpAndDie() { std::string tmp; retdec::utils::bytesToHexString(CODE, tmp, 0, 0, false, true); - std::cout << _programName << ":\n" + Log::info() << _programName << ":\n" "\t-a name Set architecture name.\n" "\t Possible values: arm, arm64, mips, x86, ppc, sparc, sysz, xcore\n" "\t Default value: x86.\n" @@ -220,8 +222,8 @@ void printVersion() int minor = 0; int version = cs_version(&major, &minor); - std::cout << std::endl; - std::cout << "Capstone version: " << version << " (major: " << major + Log::info() << std::endl; + Log::info() << "Capstone version: " << version << " (major: " << major << ", minor: " << minor << ")" << std::endl; } @@ -240,7 +242,7 @@ ks_arch capstoneArchToKeystoneArch(cs_arch a) case CS_ARCH_MAX: case CS_ARCH_ALL: default: - std::cerr << "Can not convert Capstone arch to Keystone arch." << std::endl; + Log::error() << "Can not convert Capstone arch to Keystone arch." << std::endl; exit(1); } } @@ -283,7 +285,7 @@ ks_mode capstoneModeBasicToKeystoneMode(cs_arch a, cs_mode m) } else { - std::cerr << "Can not convert Capstone basic mode to Keystone mode." << std::endl; + Log::error() << "Can not convert Capstone basic mode to Keystone mode." << std::endl; exit(1); } } @@ -316,7 +318,7 @@ ks_mode capstoneModeExtraToKeystoneMode(cs_arch a, cs_mode m) } else { - std::cerr << "Can not convert Capstone extra mode to Keystone mode." << std::endl; + Log::error() << "Can not convert Capstone extra mode to Keystone mode." << std::endl; exit(1); } } @@ -335,7 +337,7 @@ void assemble(ProgramOptions& po) if (ks_open(arch, basic | extra, &ks) != KS_ERR_OK) { ks_err err = ks_errno(ks); - std::cerr << "Keystone Error: " << ks_strerror(err) << std::endl; + Log::error() << Log::Error << "Keystone: " << ks_strerror(err) << std::endl; exit(1); } @@ -346,7 +348,7 @@ void assemble(ProgramOptions& po) if (ks_asm(ks, po.text.data(), po.base, &enc, &sz, &cnt) != KS_ERR_OK) { ks_err err = ks_errno(ks); - std::cerr << "Keystone Error: " << ks_strerror(err) << std::endl; + Log::error() << Log::Error << "Keystone: " << ks_strerror(err) << std::endl; exit(1); } @@ -361,7 +363,7 @@ void assemble(ProgramOptions& po) if (ks_close(ks) != KS_ERR_OK) { ks_err err = ks_errno(ks); - std::cerr << "Keystone Error: " << ks_strerror(err) << std::endl; + Log::error() << Log::Error << "Keystone : " << ks_strerror(err) << std::endl; exit(1); } } @@ -404,12 +406,12 @@ int main(int argc, char *argv[]) } catch (const BaseError& e) { - std::cerr << e.what() << std::endl; + Log::error() << e.what() << std::endl; assert(false); } catch (...) { - std::cerr << "Some unhandled exception" << std::endl; + Log::error() << "Some unhandled exception" << std::endl; } std::error_code ec; diff --git a/src/common/address.cpp b/src/common/address.cpp index 6edfd591f..510eb0a0c 100644 --- a/src/common/address.cpp +++ b/src/common/address.cpp @@ -7,7 +7,6 @@ #include #include #include -#include #include #include "retdec/common/address.h" diff --git a/src/config/parameters.cpp b/src/config/parameters.cpp index e49044520..e9f922de0 100644 --- a/src/config/parameters.cpp +++ b/src/config/parameters.cpp @@ -40,6 +40,8 @@ const std::string JSON_outputLlFile = "outputLlFile"; const std::string JSON_outputConfigFile = "outputConfigFile"; const std::string JSON_outputUnpackedFile = "outputUnpackedFile"; const std::string JSON_outputFormat = "outputFormat"; +const std::string JSON_logFile = "logFile"; +const std::string JSON_errFile = "errFile"; const std::string JSON_detectStaticCode = "detectStaticCode"; const std::string JSON_backendDisabledOpts = "backendDisabledOpts"; @@ -215,6 +217,16 @@ void Parameters::setOutputFormat(const std::string& format) _outputFormat = format; } +void Parameters::setLogFile(const std::string &file) +{ + _logFile = file; +} + +void Parameters::setErrFile(const std::string &file) +{ + _errFile = file; +} + void Parameters::setOrdinalNumbersDirectory(const std::string& n) { _ordinalNumbersDirectory = n; @@ -384,6 +396,16 @@ const std::string& Parameters::getOutputFormat() const return _outputFormat; } +const std::string& Parameters::getLogFile() const +{ + return _logFile; +} + +const std::string& Parameters::getErrFile() const +{ + return _errFile; +} + uint64_t Parameters::getMaxMemoryLimit() const { return _maxMemoryLimit; @@ -486,6 +508,8 @@ void Parameters::serialize(Writer& writer) const serdes::serializeString(writer, JSON_outputConfigFile, getOutputConfigFile()); serdes::serializeString(writer, JSON_outputUnpackedFile, getOutputUnpackedFile()); serdes::serializeString(writer, JSON_outputFormat, getOutputFormat()); + serdes::serializeString(writer, JSON_logFile, getLogFile()); + serdes::serializeString(writer, JSON_errFile, getErrFile()); serdes::serializeString(writer, JSON_backendDisabledOpts, getBackendDisabledOpts()); serdes::serializeString(writer, JSON_backendEnabledOpts, getBackendEnabledOpts()); @@ -553,6 +577,8 @@ void Parameters::deserialize(const rapidjson::Value& val) setOutputConfigFile( serdes::deserializeString(val, JSON_outputConfigFile) ); setOutputUnpackedFile( serdes::deserializeString(val, JSON_outputUnpackedFile) ); setOutputFormat( serdes::deserializeString(val, JSON_outputFormat) ); + setLogFile( serdes::deserializeString(val, JSON_logFile) ); + setErrFile( serdes::deserializeString(val, JSON_errFile) ); setIsDetectStaticCode( serdes::deserializeBool(val, JSON_detectStaticCode, true) ); setBackendDisabledOpts( serdes::deserializeString(val, JSON_backendDisabledOpts) ); diff --git a/src/debugformat/debugformat.cpp b/src/debugformat/debugformat.cpp index dcceb7857..b4e397ca1 100644 --- a/src/debugformat/debugformat.cpp +++ b/src/debugformat/debugformat.cpp @@ -6,7 +6,6 @@ #define LOG_ENABLED false -#include #include #include "retdec/utils/debug.h" diff --git a/src/debugformat/dwarf.cpp b/src/debugformat/dwarf.cpp index cb8875f97..387867e75 100644 --- a/src/debugformat/dwarf.cpp +++ b/src/debugformat/dwarf.cpp @@ -6,8 +6,6 @@ #define LOG_ENABLED false -#include - #include #include "retdec/demangler/demangler.h" diff --git a/src/debugformat/pdb.cpp b/src/debugformat/pdb.cpp index 2415b77e8..2397f38cd 100644 --- a/src/debugformat/pdb.cpp +++ b/src/debugformat/pdb.cpp @@ -6,8 +6,6 @@ #define LOG_ENABLED false -#include - #include "retdec/debugformat/debugformat.h" namespace retdec { diff --git a/src/demanglertool/demangler.cpp b/src/demanglertool/demangler.cpp index c2bc0eb6f..ef7a72ba1 100644 --- a/src/demanglertool/demangler.cpp +++ b/src/demanglertool/demangler.cpp @@ -9,6 +9,10 @@ #include "retdec/demangler/demangler.h" +#include "retdec/utils/io/log.h" + +using namespace retdec::utils::io; + using ItaniumDemangler = retdec::demangler::ItaniumDemangler; using MicrosoftDemangler = retdec::demangler::MicrosoftDemangler; using BorlandDemangler = retdec::demangler::BorlandDemangler; @@ -35,7 +39,7 @@ int main(int argc, char *argv[]) std::string demangledBorland; if (argc <= 1 || strcmp(argv[1], "-h") == 0 || strcmp(argv[1], "--help") == 0) { - std::cout << helpmsg; + Log::info() << helpmsg; return 0; } @@ -47,13 +51,13 @@ int main(int argc, char *argv[]) demangledBorland = dem_borland->demangleToString(argv[i]); if (!demangledGcc.empty()) { - std::cout << "gcc: " << demangledGcc << std::endl; + Log::info() << "gcc: " << demangledGcc << std::endl; } if (!demangledMs.empty()) { - std::cout << "ms: " << demangledMs << std::endl; + Log::info() << "ms: " << demangledMs << std::endl; } if (!demangledBorland.empty()) { - std::cout << "borland: " << demangledBorland << std::endl; + Log::info() << "borland: " << demangledBorland << std::endl; } } diff --git a/src/fileformat/file_format/file_format.cpp b/src/fileformat/file_format/file_format.cpp index f0c74dc1e..70aeb87a3 100644 --- a/src/fileformat/file_format/file_format.cpp +++ b/src/fileformat/file_format/file_format.cpp @@ -9,13 +9,13 @@ #include #include #include -#include #include #include "retdec/utils/conversion.h" #include "retdec/utils/file_io.h" #include "retdec/utils/string.h" #include "retdec/utils/system.h" +#include "retdec/utils/io/log.h" #include "retdec/fileformat/file_format/file_format.h" #include "retdec/fileformat/utils/byte_array_buffer.h" #include "retdec/fileformat/file_format/intel_hex/intel_hex_format.h" @@ -28,6 +28,7 @@ #include "retdec/pelib/PeLibInc.h" using namespace retdec::utils; +using namespace retdec::utils::io; using namespace PeLib; namespace retdec { @@ -2336,7 +2337,7 @@ void FileFormat::dump() { std::string output; dump(output); - std::cout << output; + Log::info() << output; } /** @@ -2561,7 +2562,7 @@ void FileFormat::dumpRegionsValidity() { std::string output; dumpRegionsValidity(output); - std::cout << output; + Log::info() << output; } /** @@ -2603,7 +2604,7 @@ void FileFormat::dumpResourceTree() { std::string output; dumpResourceTree(output); - std::cout << output; + Log::info() << output; } void FileFormat::dumpResourceTree(std::string &dumpStr) diff --git a/src/fileformat/types/dotnet_types/dotnet_type_reconstructor.cpp b/src/fileformat/types/dotnet_types/dotnet_type_reconstructor.cpp index 89eb68dfd..e498cbd2e 100644 --- a/src/fileformat/types/dotnet_types/dotnet_type_reconstructor.cpp +++ b/src/fileformat/types/dotnet_types/dotnet_type_reconstructor.cpp @@ -4,8 +4,6 @@ * @copyright (c) 2017 Avast Software, licensed under the MIT license */ -#include - #include "retdec/utils/conversion.h" #include "retdec/utils/string.h" #include "retdec/fileformat/types/dotnet_headers/metadata_tables.h" diff --git a/src/fileformat/types/resource_table/resource_table.cpp b/src/fileformat/types/resource_table/resource_table.cpp index 6874e07bd..aab94fb5a 100644 --- a/src/fileformat/types/resource_table/resource_table.cpp +++ b/src/fileformat/types/resource_table/resource_table.cpp @@ -5,7 +5,6 @@ */ #include -#include #include "retdec/utils/conversion.h" #include "retdec/utils/dynamic_buffer.h" diff --git a/src/fileformat/utils/byte_array_buffer.cpp b/src/fileformat/utils/byte_array_buffer.cpp index a42472911..c40da9d8e 100644 --- a/src/fileformat/utils/byte_array_buffer.cpp +++ b/src/fileformat/utils/byte_array_buffer.cpp @@ -4,8 +4,6 @@ * @copyright (c) 2019 Avast Software, licensed under the MIT license */ -#include - #include #include #include diff --git a/src/fileinfo/file_presentation/json_presentation.cpp b/src/fileinfo/file_presentation/json_presentation.cpp index de25779eb..d2597e71e 100644 --- a/src/fileinfo/file_presentation/json_presentation.cpp +++ b/src/fileinfo/file_presentation/json_presentation.cpp @@ -4,10 +4,9 @@ * @copyright (c) 2017 Avast Software, licensed under the MIT license */ -#include - #include "retdec/utils/conversion.h" #include "retdec/utils/string.h" +#include "retdec/utils/io/log.h" #include "retdec/fileformat/utils/conversions.h" #include "retdec/serdes/pattern.h" #include "retdec/serdes/std.h" @@ -17,6 +16,7 @@ using namespace retdec; using namespace retdec::utils; +using namespace retdec::utils::io; using namespace retdec::cpdetect; using namespace retdec::fileformat; @@ -1285,7 +1285,7 @@ bool JsonPresentation::present() presentIterativeSubtitle(writer, StringsJsonGetter(fileinfo)); writer.EndObject(); - std::cout << sb.GetString() << std::endl; + Log::info() << sb.GetString() << std::endl; return true; } diff --git a/src/fileinfo/file_presentation/plain_presentation.cpp b/src/fileinfo/file_presentation/plain_presentation.cpp index d78140b5d..dbea59ebb 100644 --- a/src/fileinfo/file_presentation/plain_presentation.cpp +++ b/src/fileinfo/file_presentation/plain_presentation.cpp @@ -4,15 +4,15 @@ * @copyright (c) 2017 Avast Software, licensed under the MIT license */ -#include - #include "retdec/utils/string.h" +#include "retdec/utils/io/log.h" #include "retdec/fileformat/utils/conversions.h" #include "fileinfo/file_presentation/getters/format.h" #include "fileinfo/file_presentation/getters/plain_getters.h" #include "fileinfo/file_presentation/plain_presentation.h" using namespace retdec::utils; +using namespace retdec::utils::io; using namespace retdec::cpdetect; using namespace retdec::fileformat; @@ -33,7 +33,7 @@ void presentTitle(const std::string &title) const auto pos = title.find_first_not_of(" "); if(pos != std::string::npos) { - std::cout << "\n\n" << title << "\n" << std::string(pos, ' ') << std::string(title.length() - pos, '-') << "\n"; + Log::info() << "\n\n" << title << "\n" << std::string(pos, ' ') << std::string(title.length() - pos, '-') << "\n"; } } @@ -62,10 +62,10 @@ void presentSimple(const std::vector &desc, const std::vector(tool.agreeCount) / tool.impCount * 100; - std::cout << ", " << tool.agreeCount << " from " << tool.impCount << " significant " << nibbles; - std::cout << " (" << ratio << "%)"; + Log::info() << ", " << tool.agreeCount << " from " << tool.impCount << " significant " << nibbles; + Log::info() << " (" << ratio << "%)"; } else { - std::cout << ", " << detectionMetodToString(tool.source); + Log::info() << ", " << detectionMetodToString(tool.source); } - std::cout << "\n"; + Log::info() << "\n"; } } @@ -394,26 +394,26 @@ void PlainPresentation::presentLanguages() const { return; } - std::cout << "Original language : "; + Log::info() << "Original language : "; for(std::size_t i = 0; i < noOfLanguages; ) { - std::cout << fileinfo.toolInfo.detectedLanguages[i].name; + Log::info() << fileinfo.toolInfo.detectedLanguages[i].name; if(fileinfo.toolInfo.detectedLanguages[i].additionalInfo.length()) { - std::cout << " (" << fileinfo.toolInfo.detectedLanguages[i].additionalInfo << ")"; + Log::info() << " (" << fileinfo.toolInfo.detectedLanguages[i].additionalInfo << ")"; } if(fileinfo.toolInfo.detectedLanguages[i].bytecode) { - std::cout << " (bytecode)"; + Log::info() << " (bytecode)"; } if(++i < noOfLanguages) { - std::cout << ", "; + Log::info() << ", "; } } - std::cout << "\n"; + Log::info() << "\n"; } /** @@ -426,11 +426,11 @@ void PlainPresentation::presentRichHeader() const const auto sig = toLower(fileinfo.getRichHeaderSignature()); if(!offset.empty()) { - std::cout << "Rich header offset : " << offset << "\n"; + Log::info() << "Rich header offset : " << offset << "\n"; } if(!key.empty()) { - std::cout << "Rich header key : " << key << "\n"; + Log::info() << "Rich header key : " << key << "\n"; } if(!sig.empty()) { @@ -441,7 +441,7 @@ void PlainPresentation::presentRichHeader() const for(std::size_t i = 0, e = sig.length(); i < e; i += signLineLen) { - std::cout << (i ? std::string(signDesc.length(), ' ') : signDesc) << sig.substr(i, signLineLen) << "\n"; + Log::info() << (i ? std::string(signDesc.length(), ' ') : signDesc) << sig.substr(i, signLineLen) << "\n"; } } } @@ -456,15 +456,15 @@ void PlainPresentation::presentOverlay() const const auto entropy = fileinfo.getOverlayEntropyStr(truncFloat); if(!offset.empty()) { - std::cout << "Overlay offset : " << offset << "\n"; + Log::info() << "Overlay offset : " << offset << "\n"; } if(!size.empty()) { - std::cout << "Overlay size : " << size << "\n"; + Log::info() << "Overlay size : " << size << "\n"; } if(!entropy.empty()) { - std::cout << "Overlay entropy : " << entropy << "\n"; + Log::info() << "Overlay entropy : " << entropy << "\n"; } } @@ -474,7 +474,7 @@ void PlainPresentation::presentOverlay() const void PlainPresentation::presentPackingInfo() const { const auto packed = fileinfo.toolInfo.isPacked(); - std::cout << "Packed : " << packedToString(packed) << "\n"; + Log::info() << "Packed : " << packedToString(packed) << "\n"; } /** @@ -491,18 +491,18 @@ void PlainPresentation::presentSimpleFlags(const std::string &title, const std:: return; } - std::cout << title << flags; + Log::info() << title << flags; const std::string abbreviations = abbvSerialization(abbv); if(!abbreviations.empty()) { - flags.empty() ? std::cout << abbreviations : std::cout << " (" << abbreviations << ")"; + flags.empty() ? Log::info() << abbreviations : Log::info() << " (" << abbreviations << ")"; } - std::cout << "\n"; + Log::info() << "\n"; if(explanatory) { for(std::size_t i = 0, e = abbv.size(); i < e; ++i) { - std::cout << " " << abbv[i] << " - " << desc[i] << "\n"; + Log::info() << " " << abbv[i] << " - " << desc[i] << "\n"; } } } @@ -520,31 +520,31 @@ void PlainPresentation::presentPatterns(const std::string &title, const std::vec } presentTitle(title); - std::cout << "Number of detected patterns: " << patterns.size() << "\n\n"; + Log::info() << "Number of detected patterns: " << patterns.size() << "\n\n"; for(std::size_t i = 0, e = patterns.size(); i < e; ++i) { - std::cout << patterns[i].getYaraRuleName() << "\n"; + Log::info() << patterns[i].getYaraRuleName() << "\n"; const auto description = patterns[i].getDescription(); if(!description.empty()) { - std::cout << " description: " << description << "\n"; + Log::info() << " description: " << description << "\n"; } if(patterns[i].isLittle() || patterns[i].isBig()) { const std::string end = patterns[i].isLittle() ? "little" : "big"; - std::cout << " endianness: " << end << "\n"; + Log::info() << " endianness: " << end << "\n"; } - std::cout << " number of matches: " << patterns[i].getNumberOfMatches(); + Log::info() << " number of matches: " << patterns[i].getNumberOfMatches(); const auto &matches = patterns[i].getMatches(); presentIterativeDistribution(PatternMatchesPlainGetter(fileinfo, matches), explanatory); if(matches.empty()) { - std::cout << "\n"; + Log::info() << "\n"; } if(i + 1 != e) { - std::cout << "\n"; + Log::info() << "\n"; } } } @@ -555,10 +555,10 @@ void PlainPresentation::presentDotnetClasses() const if (classes.empty()) return; - std::cout << '\n'; + Log::info() << '\n'; for (const auto& dotnetClass : classes) { - std::cout << dotnetClass->getVisibilityString() << ' ' + Log::info() << dotnetClass->getVisibilityString() << ' ' << (dotnetClass->isAbstract() ? "abstract " : "") << (dotnetClass->isSealed() ? "sealed " : "") << dotnetClass->getTypeString() << ' ' @@ -566,23 +566,23 @@ void PlainPresentation::presentDotnetClasses() const if (!dotnetClass->getBaseTypes().empty()) { - std::cout << " : "; + Log::info() << " : "; for (auto itr = dotnetClass->getBaseTypes().begin(), end = dotnetClass->getBaseTypes().end(); itr != end; ++itr) { - std::cout << (*itr)->getText(); + Log::info() << (*itr)->getText(); if (itr + 1 != end) - std::cout << ", "; + Log::info() << ", "; } } - std::cout << '\n'; + Log::info() << '\n'; if (!dotnetClass->getMethods().empty()) - std::cout << " // Methods\n"; + Log::info() << " // Methods\n"; for (const auto& dotnetMethod : dotnetClass->getMethods()) { - std::cout << " " << dotnetMethod->getVisibilityString() << ' ' + Log::info() << " " << dotnetMethod->getVisibilityString() << ' ' << (dotnetMethod->isStatic() ? "static " : "") << (dotnetMethod->isVirtual() ? "virtual " : "") << (dotnetMethod->isFinal() ? "sealed " : "") @@ -593,31 +593,31 @@ void PlainPresentation::presentDotnetClasses() const for (auto itr = dotnetMethod->getParameters().begin(), end = dotnetMethod->getParameters().end(); itr != end; ++itr) { - std::cout << (*itr)->getDataType()->getText() << ' ' << (*itr)->getName(); + Log::info() << (*itr)->getDataType()->getText() << ' ' << (*itr)->getName(); if (itr + 1 != end) - std::cout << ", "; + Log::info() << ", "; } - std::cout << ")\n"; + Log::info() << ")\n"; } if (!dotnetClass->getFields().empty()) - std::cout << " // Fields\n"; + Log::info() << " // Fields\n"; for (const auto& dotnetField : dotnetClass->getFields()) { - std::cout << " " << dotnetField->getVisibilityString() << ' ' + Log::info() << " " << dotnetField->getVisibilityString() << ' ' << dotnetField->getDataType()->getText() << ' ' << dotnetField->getName() << '\n'; } if (!dotnetClass->getProperties().empty()) - std::cout << " // Properties\n"; + Log::info() << " // Properties\n"; for (const auto& dotnetProperty : dotnetClass->getProperties()) { - std::cout << " " << dotnetProperty->getVisibilityString() << ' ' + Log::info() << " " << dotnetProperty->getVisibilityString() << ' ' << dotnetProperty->getDataType()->getText() << ' ' << dotnetProperty->getName() << '\n'; @@ -634,14 +634,14 @@ void PlainPresentation::presentVisualBasicObjects() const return; } - std::cout << "\n"; - std::cout << "Visual Basic Object table" << "\n"; - std::cout << "-------------------------" << "\n"; - std::cout << "CRC32 : " << fileinfo.getVisualBasicObjectTableHashCrc32() << "\n"; - std::cout << "MD5 : " << fileinfo.getVisualBasicObjectTableHashMd5() << "\n"; - std::cout << "SHA256 : " << fileinfo.getVisualBasicObjectTableHashSha256() << "\n"; - std::cout << "GUID : " << guid << "\n"; - std::cout << "\n"; + Log::info() << "\n"; + Log::info() << "Visual Basic Object table" << "\n"; + Log::info() << "-------------------------" << "\n"; + Log::info() << "CRC32 : " << fileinfo.getVisualBasicObjectTableHashCrc32() << "\n"; + Log::info() << "MD5 : " << fileinfo.getVisualBasicObjectTableHashMd5() << "\n"; + Log::info() << "SHA256 : " << fileinfo.getVisualBasicObjectTableHashSha256() << "\n"; + Log::info() << "GUID : " << guid << "\n"; + Log::info() << "\n"; std::size_t cnt = 0; for (std::size_t i = 0; i < nObjs; i++) @@ -656,10 +656,10 @@ void PlainPresentation::presentVisualBasicObjects() const { continue; } - std::cout << cnt << ". " << "object name: " << objName << "\n"; + Log::info() << cnt << ". " << "object name: " << objName << "\n"; for (const auto &m : obj->getMethods()) { - std::cout << " method name: " << m << "\n"; + Log::info() << " method name: " << m << "\n"; } cnt++; } @@ -698,7 +698,7 @@ void PlainPresentation::presentCore() const bool PlainPresentation::present() { - std::cout << "Input file : " << fileinfo.getPathToFile() << "\n"; + Log::info() << "Input file : " << fileinfo.getPathToFile() << "\n"; presentSimple(BasicPlainGetter(fileinfo), false); presentCompiler(); presentLanguages(); @@ -706,12 +706,12 @@ bool PlainPresentation::present() presentOverlay(); if(returnCode != ReturnCode::OK) { - std::cerr << getErrorMessage(returnCode, fileinfo.getFileFormatEnum()) << "\n"; + Log::error() << getErrorMessage(returnCode, fileinfo.getFileFormatEnum()) << "\n"; } for(std::size_t i = 0, e = fileinfo.messages.size(); i < e; ++i) { - std::cerr << fileinfo.messages[i] << "\n"; + Log::error() << fileinfo.messages[i] << "\n"; } if(verbose) @@ -721,13 +721,13 @@ bool PlainPresentation::present() errorMessage = fileinfo.getLoaderStatusMessage(); if(!errorMessage.empty()) { - std::cerr << "Warning: " << errorMessage << "\n"; + Log::error() << Log::Warning << errorMessage << "\n"; } errorMessage = fileinfo.getDepsListFailedToLoad(); if (!errorMessage.empty()) { - std::cerr << "Warning: Failed to load the dependency list (\"" << errorMessage << "\")\n"; + Log::error() << Log::Warning << "Failed to load the dependency list (\"" << errorMessage << "\")\n"; } std::string flags, title; @@ -768,13 +768,13 @@ bool PlainPresentation::present() presentTitle("Manifest"); if(manifest[0] != '\n') { - std::cout << "\n"; + Log::info() << "\n"; } if(manifest[manifest.length() - 1] != '\n') { manifest += '\n'; } - std::cout << replaceNonasciiChars(manifest); + Log::info() << replaceNonasciiChars(manifest); } presentIterativeSimple(CertificateTablePlainGetter(fileinfo)); diff --git a/src/fileinfo/fileinfo.cpp b/src/fileinfo/fileinfo.cpp index 5a8b52aa8..a08807880 100644 --- a/src/fileinfo/fileinfo.cpp +++ b/src/fileinfo/fileinfo.cpp @@ -4,13 +4,13 @@ * @copyright (c) 2017 Avast Software, licensed under the MIT license */ -#include #include #include #include "retdec/utils/conversion.h" #include "retdec/utils/memory.h" +#include "retdec/utils/io/log.h" #include "retdec/utils/string.h" #include "retdec/ar-extractor/detection.h" #include "retdec/cpdetect/errors.h" @@ -25,6 +25,7 @@ #include "fileinfo/pattern_detector/pattern_detector.h" using namespace retdec::utils; +using namespace retdec::utils::io; using namespace retdec::ar_extractor; using namespace retdec::cpdetect; using namespace retdec::fileformat; @@ -108,7 +109,7 @@ void fatalErrorHandler(void *user_data, const std::string& /*reason*/, bool /*ge */ void printHelp() { - std::cout << "fileinfo - dumper of information about executable file\n\n" + Log::info() << "fileinfo - dumper of information about executable file\n\n" << "For compiler detection, program looks in the input file for YARA patterns.\n" << "According to them, it determines compiler or packer used for file creation.\n" << "Supported file formats are: " + joinStrings(getSupportedFileFormats()) + ".\n\n" @@ -188,7 +189,7 @@ std::string getParamOrDie(std::vector &argv, std::size_t &i) } else { - std::cerr << getErrorMessage(ReturnCode::ARG) << "\n\n"; + Log::error() << getErrorMessage(ReturnCode::ARG) << "\n\n"; printHelp(); exit(static_cast(ReturnCode::ARG)); } @@ -413,7 +414,7 @@ int main(int argc, char* argv[]) ProgParams params; if(!doParams(argc, argv, params)) { - std::cerr << getErrorMessage(ReturnCode::ARG) << "\n\n"; + Log::error() << getErrorMessage(ReturnCode::ARG) << "\n\n"; printHelp(); return static_cast(ReturnCode::ARG); } @@ -517,7 +518,7 @@ int main(int argc, char* argv[]) auto config = ConfigPresentation(fileinfo, params.configFile); if(!config.present()) { - std::cerr << "Error: loading of config failed: " << config.getErrorMessage() << "\n"; + Log::error() << "Error: loading of config failed: " << config.getErrorMessage() << "\n"; res = ReturnCode::FILE_PROBLEM; } } diff --git a/src/getsig/getsig.cpp b/src/getsig/getsig.cpp index 78794857b..fbf42782f 100644 --- a/src/getsig/getsig.cpp +++ b/src/getsig/getsig.cpp @@ -5,22 +5,23 @@ */ #include -#include #include #include "retdec/fileformat/format_factory.h" #include "retdec/utils/conversion.h" #include "retdec/utils/string.h" +#include "retdec/utils/io/log.h" using namespace retdec::fileformat; using namespace retdec::utils; +using namespace retdec::utils::io; /** * Print usage. */ void printUsage() { - std::cout << + Log::info() << "getsig - generator of YARA tool signatures\n\n" "Program takes executable files, compares their content at given\n" "offset and prints signature representing contents of all files.\n\n" @@ -93,7 +94,7 @@ struct Options int printError( const std::string& message) { - std::cerr << "Error: " << message << ".\n"; + Log::error() << Log::Error << message << ".\n"; return 1; } @@ -104,7 +105,7 @@ int printError( void printWarning( const std::string& message) { - std::cerr << "Warning: " << message << ".\n"; + Log::error() << Log::Warning << message << ".\n"; } /** @@ -121,7 +122,7 @@ std::string getParamOrDie(std::vector &argv, std::size_t &i) } else { - std::cerr << "Error: missing argument value.\n\n"; + Log::error() << Log::Error << "missing argument value.\n\n"; printUsage(); exit(1); } @@ -512,7 +513,7 @@ int main(int argc, char** argv) { // create detection rule const auto rule = getYaraRule(signature, format, options); - std::cout << rule; + Log::info() << rule; if (!options.outputFile.empty()) { diff --git a/src/idr2pat/idr2pat.cpp b/src/idr2pat/idr2pat.cpp index 50e86c2e3..8e42cad4c 100644 --- a/src/idr2pat/idr2pat.cpp +++ b/src/idr2pat/idr2pat.cpp @@ -7,18 +7,19 @@ #include #include #include -#include #include #include #include #include #include "retdec/utils/conversion.h" +#include "retdec/utils/io/log.h" #include "yaramod/builder/yara_expression_builder.h" #include "yaramod/builder/yara_hex_string_builder.h" #include "yaramod/builder/yara_rule_builder.h" using namespace retdec::utils; +using namespace retdec::utils::io; using namespace yaramod; /** @@ -217,7 +218,7 @@ void readFunction( ruleBuilder.withHexString("$1", hexBuilder.get()); ruleBuilder.withCondition(stringRef("$1").get()); - std::cout << ruleBuilder.get()->getText() << "\n"; + Log::info() << ruleBuilder.get()->getText() << "\n"; } /** @@ -298,7 +299,7 @@ bool readDatabase( */ int printError(const std::string &message) { - std::cerr << "Error: " << message << ".\n"; + Log::error() << Log::Error << message << ".\n"; return 1; } diff --git a/src/llvm-support/CMakeLists.txt b/src/llvm-support/CMakeLists.txt deleted file mode 100644 index f1e4de11f..000000000 --- a/src/llvm-support/CMakeLists.txt +++ /dev/null @@ -1,58 +0,0 @@ - -add_library(llvm-support STATIC - diagnostics.cpp -) -add_library(retdec::llvm-support ALIAS llvm-support) - -target_compile_features(llvm-support PUBLIC cxx_std_17) - -target_include_directories(llvm-support - PUBLIC - $ - $ -) - -target_link_libraries(llvm-support - PUBLIC - retdec::deps::llvm - PRIVATE - retdec::utils -) - -set_target_properties(llvm-support - PROPERTIES - OUTPUT_NAME "retdec-llvm-support" -) - -# Install includes. -install( - DIRECTORY ${RETDEC_INCLUDE_DIR}/retdec/llvm-support - DESTINATION ${RETDEC_INSTALL_INCLUDE_DIR}/retdec -) - -# Install libs. -install(TARGETS llvm-support - EXPORT llvm-support-targets - ARCHIVE DESTINATION ${RETDEC_INSTALL_LIB_DIR} - LIBRARY DESTINATION ${RETDEC_INSTALL_LIB_DIR} -) - -# Export targets. -install(EXPORT llvm-support-targets - FILE "retdec-llvm-support-targets.cmake" - NAMESPACE retdec:: - DESTINATION ${RETDEC_INSTALL_CMAKE_DIR} -) - -# Install CMake files. -configure_file( - "retdec-llvm-support-config.cmake" - "${CMAKE_CURRENT_BINARY_DIR}/retdec-llvm-support-config.cmake" - @ONLY -) -install( - FILES - "${CMAKE_CURRENT_BINARY_DIR}/retdec-llvm-support-config.cmake" - DESTINATION - "${RETDEC_INSTALL_CMAKE_DIR}" -) diff --git a/src/llvm-support/diagnostics.cpp b/src/llvm-support/diagnostics.cpp deleted file mode 100644 index 555398d48..000000000 --- a/src/llvm-support/diagnostics.cpp +++ /dev/null @@ -1,116 +0,0 @@ -/** -* @file src/llvm-support/diagnostics.cpp -* @brief Implementation of the functions concerning emission of diagnostics -* messages, like error or warning messages. -* @copyright (c) 2017 Avast Software, licensed under the MIT license -*/ - -#include -#include - -#include "retdec/llvm-support/diagnostics.h" -#include "retdec/utils/conversion.h" -#include "retdec/utils/time.h" - -using retdec::utils::getElapsedTime; - -namespace retdec { -namespace llvm_support { - -namespace { - -/** -* @brief Formats the given elapsed time so it can be printed. -* -* The resulting string is a representation of @a elapsedTime with fixed two -* numbers after the decimal point. For example, number @c 1.16397 is formatted -* as @c 1.16. -*/ -std::string formatElapsedTime(double elapsedTime) { - std::stringstream formatted; - formatted << std::fixed << std::setprecision(2) << elapsedTime; - return formatted.str(); -} - -/** -* @brief Prints the given phase to the given stream with the given prefix. -* -* This function is used to implement the print*Phase() functions. -*/ -void printPrefixedPhase(const std::string &prefix, const std::string &phaseName, - llvm::raw_ostream &stream) { - printColoredLine(stream, llvm::raw_ostream::YELLOW, /* bold */ true, prefix, - phaseName, " ( ", formatElapsedTime(getElapsedTime()), "s )"); - - // Make it appear as soon as possible to keep the user updated. - stream.flush(); -} - -} // anonymous namespace - -/** -* @brief Prints the given phase to the given stream. -* -* A new line is appended after the emitted text and the stream is flushed. -*/ -void printPhase( - const std::string &phaseName, - bool print, - llvm::raw_ostream &stream) -{ - if (print) - { - printPrefixedPhase("Running phase: ", phaseName, stream); - } -} - -/** -* @brief Prints the given sub-phase to the given stream. -* -* A new line is appended after the emitted text and the stream is flushed. -*/ -void printSubPhase( - const std::string &phaseName, - bool print, - llvm::raw_ostream &stream) -{ - if (print) - { - printPrefixedPhase(" -> ", phaseName, stream); - } -} - -/** -* @brief Prints the given sub-sub-phase to the given stream. -* -* A new line is appended after the emitted text and the stream is flushed. -*/ -void printSubSubPhase( - const std::string &phaseName, - bool print, - llvm::raw_ostream &stream) -{ - if (print) - { - printPrefixedPhase(" -> ", phaseName, stream); - } -} - -/** -* @brief Prints the given sub-sub-sub-phase to the given stream. -* -* A new line is appended after the emitted text and the stream is flushed. -*/ -void printSubSubSubPhase( - const std::string &phaseName, - bool print, - llvm::raw_ostream &stream) -{ - if (print) - { - printPrefixedPhase(" -> ", phaseName, stream); - } -} - -} // namespace llvm_support -} // namespace retdec diff --git a/src/llvm-support/retdec-llvm-support-config.cmake b/src/llvm-support/retdec-llvm-support-config.cmake deleted file mode 100644 index 7f9d08996..000000000 --- a/src/llvm-support/retdec-llvm-support-config.cmake +++ /dev/null @@ -1,11 +0,0 @@ - -if(NOT TARGET retdec::llvm-support) - find_package(retdec @PROJECT_VERSION@ - REQUIRED - COMPONENTS - utils - llvm - ) - - include(${CMAKE_CURRENT_LIST_DIR}/retdec-llvm-support-targets.cmake) -endif() diff --git a/src/llvmir-emul/llvmir_emul.cpp b/src/llvmir-emul/llvmir_emul.cpp index 0ff55faef..558751b7b 100644 --- a/src/llvmir-emul/llvmir_emul.cpp +++ b/src/llvmir-emul/llvmir_emul.cpp @@ -4,8 +4,6 @@ * @copyright (c) 2017 Avast Software, licensed under the MIT license */ -#include - #include #include #include diff --git a/src/llvmir2hll/CMakeLists.txt b/src/llvmir2hll/CMakeLists.txt index 6573f63e0..36985792a 100644 --- a/src/llvmir2hll/CMakeLists.txt +++ b/src/llvmir2hll/CMakeLists.txt @@ -310,7 +310,6 @@ target_link_libraries(llvmir2hll PUBLIC retdec::config retdec::utils - retdec::llvm-support retdec::deps::rapidjson retdec::deps::llvm ) diff --git a/src/llvmir2hll/graphs/cfg/cfg_builders/non_recursive_cfg_builder.cpp b/src/llvmir2hll/graphs/cfg/cfg_builders/non_recursive_cfg_builder.cpp index 0113e3c20..c110ddf04 100644 --- a/src/llvmir2hll/graphs/cfg/cfg_builders/non_recursive_cfg_builder.cpp +++ b/src/llvmir2hll/graphs/cfg/cfg_builders/non_recursive_cfg_builder.cpp @@ -28,11 +28,10 @@ #include "retdec/llvmir2hll/support/debug.h" #include "retdec/llvmir2hll/support/expression_negater.h" #include "retdec/llvmir2hll/utils/ir.h" -#include "retdec/llvm-support/diagnostics.h" #include "retdec/utils/container.h" +#include "retdec/utils/io/log.h" -using namespace retdec::llvm_support; - +using namespace retdec::utils::io; using retdec::utils::clear; namespace retdec { @@ -392,8 +391,8 @@ void NonRecursiveCFGBuilder::addEdgeFromVector(const EdgeToAdd &edge) { // TODO This is the same problem as in the TODO below. if (i == emptyStmtToNodeMap.end()) { - printWarningMessage("[NonRecursiveCFGBuilder] there is no node for" - " an edge to `", edge.succStmt, "` -> skipping this edge"); + Log::error() << Log::Warning << "[NonRecursiveCFGBuilder] there is no node for" + " an edge to `" << edge.succStmt << "` -> skipping this edge" << std::endl; return; } @@ -431,8 +430,8 @@ void NonRecursiveCFGBuilder::addEdgeFromVector(const EdgeToAdd &edge) { // - binaries-suite/arm-elf/O2/gnuarm-elf-gcc-O2--gzip // if (!targetNode) { - printWarningMessage("[NonRecursiveCFGBuilder] there is no node for" - " an edge to `", edge.succStmt, "` -> skipping this edge"); + Log::error() << Log::Warning << "[NonRecursiveCFGBuilder] there is no node for" + " an edge to `" << edge.succStmt << "` -> skipping this edge" << std::endl; return; } diff --git a/src/llvmir2hll/hll/hll_writers/c_hll_writer.cpp b/src/llvmir2hll/hll/hll_writers/c_hll_writer.cpp index 57c741c10..86d7c6afe 100644 --- a/src/llvmir2hll/hll/hll_writers/c_hll_writer.cpp +++ b/src/llvmir2hll/hll/hll_writers/c_hll_writer.cpp @@ -100,12 +100,9 @@ #include "retdec/llvmir2hll/support/struct_types_sorter.h" #include "retdec/llvmir2hll/support/types.h" #include "retdec/llvmir2hll/utils/ir.h" -#include "retdec/llvm-support/diagnostics.h" #include "retdec/utils/container.h" #include "retdec/utils/conversion.h" -using namespace retdec::llvm_support; - using retdec::utils::addToSet; namespace retdec { diff --git a/src/llvmir2hll/llvm/llvmir2bir_converter.cpp b/src/llvmir2hll/llvm/llvmir2bir_converter.cpp index ccb4d0891..612956350 100644 --- a/src/llvmir2hll/llvm/llvmir2bir_converter.cpp +++ b/src/llvmir2hll/llvm/llvmir2bir_converter.cpp @@ -6,7 +6,6 @@ #include -#include "retdec/llvm-support/diagnostics.h" #include "retdec/llvmir2hll/config/config.h" #include "retdec/llvmir2hll/ir/expression.h" #include "retdec/llvmir2hll/ir/function.h" @@ -20,8 +19,9 @@ #include "retdec/llvmir2hll/support/debug.h" #include "retdec/llvmir2hll/utils/ir.h" #include "retdec/llvmir2hll/utils/string.h" +#include "retdec/utils/io/log.h" -using namespace retdec::llvm_support; +using namespace retdec::utils::io; namespace retdec { namespace llvmir2hll { @@ -148,7 +148,7 @@ ShPtr LLVMIR2BIRConverter::convertGlobalVariableInitializer( */ void LLVMIR2BIRConverter::convertAndAddGlobalVariables() { if (enableDebug) { - printSubPhase("converting global variables"); + Log::phase("converting global variables", Log::SubPhase); } for (auto &globVar: llvmModule->globals()) { @@ -198,7 +198,7 @@ ShPtr LLVMIR2BIRConverter::convertFuncDeclaration( void LLVMIR2BIRConverter::updateFuncToDefinition(llvm::Function &func) { auto name = func.getName(); if (enableDebug) { - printSubPhase("converting function " + name.str()); + Log::phase("converting function " + name.str(), Log::SubPhase); } auto birFunc = resModule->getFuncByName(name); diff --git a/src/llvmir2hll/llvm/llvmir2bir_converter/labels_handler.cpp b/src/llvmir2hll/llvm/llvmir2bir_converter/labels_handler.cpp index 3cbd7132b..05c88439c 100644 --- a/src/llvmir2hll/llvm/llvmir2bir_converter/labels_handler.cpp +++ b/src/llvmir2hll/llvm/llvmir2bir_converter/labels_handler.cpp @@ -4,8 +4,6 @@ * @copyright (c) 2017 Avast Software, licensed under the MIT license */ -// #include - #include #include "retdec/llvmir2hll/ir/statement.h" diff --git a/src/llvmir2hll/llvmir2hll.cpp b/src/llvmir2hll/llvmir2hll.cpp index 056363429..bd176dde0 100644 --- a/src/llvmir2hll/llvmir2hll.cpp +++ b/src/llvmir2hll/llvmir2hll.cpp @@ -1,13 +1,13 @@ #include -#include #include #include #include "retdec/llvmir2hll/llvmir2hll.h" -#include "retdec/llvm-support/diagnostics.h" +#include "retdec/utils/io/log.h" using namespace llvm; +using namespace retdec::utils::io; using retdec::llvmir2hll::ShPtr; using retdec::utils::hasItem; using retdec::utils::joinStrings; @@ -50,7 +50,7 @@ std::unique_ptr getOutputStream( ); if (ec) { - errs() << ec.message() << '\n'; + Log::error() << ec.message() << '\n'; return {}; } return out; @@ -90,21 +90,18 @@ void printErrorUnsupportedObject( std::string supportedObjects(getListOfSupportedObjects()); if (!supportedObjects.empty()) { - retdec::llvm_support::printErrorMessage( - "Invalid name of the ", - typeOfObjectsSingular, - " (supported names are: ", - supportedObjects, - ")." - ); + Log::error() << Log::Error + << "Invalid name of the " << typeOfObjectsSingular + << " (supported names are: " << supportedObjects << ")." + << std::endl; } else { - retdec::llvm_support::printErrorMessage( - "There are no available ", - typeOfObjectsPlural, - ". Please SHIT, recompile the backend and try it again." - ); + Log::error() << Log::Error + << "There are no available " + << typeOfObjectsPlural + << ". Please SHIT, recompile the backend and try it again." + << std::endl; } } @@ -152,7 +149,7 @@ void LlvmIr2Hll::getAnalysisUsage(llvm::AnalysisUsage &au) const bool LlvmIr2Hll::runOnModule(llvm::Module &m) { - llvm_support::printPhase("initialization", Debug); + Log::phase("initialization"); bool decompilationShouldContinue = initialize(m); if (!decompilationShouldContinue) @@ -160,7 +157,7 @@ bool LlvmIr2Hll::runOnModule(llvm::Module &m) return false; } - llvm_support::printPhase("conversion of LLVM IR into BIR", Debug); + Log::phase("conversion of LLVM IR into BIR"); decompilationShouldContinue = convertLLVMIRToBIR(); if (!decompilationShouldContinue) { @@ -169,10 +166,7 @@ bool LlvmIr2Hll::runOnModule(llvm::Module &m) if (!globalConfig->parameters.isBackendKeepLibraryFuncs()) { - llvm_support::printPhase( - "removing functions from standard libraries", - Debug - ); + Log::phase("removing functions from standard libraries"); removeLibraryFuncs(); } @@ -181,94 +175,73 @@ bool LlvmIr2Hll::runOnModule(llvm::Module &m) // the conversion of LLVM IR to BIR is not perfect, so it may introduce // unreachable code. This causes problems later during optimizations // because the code exists in BIR, but not in a CFG. - llvm_support::printPhase( - "removing code that is not reachable in a CFG", - Debug - ); + Log::phase("removing code that is not reachable in a CFG"); removeCodeUnreachableInCFG(); - llvm_support::printPhase("signed/unsigned types fixing", Debug); + Log::phase("signed/unsigned types fixing"); fixSignedUnsignedTypes(); - llvm_support::printPhase( - "converting LLVM intrinsic functions to standard functions", - Debug - ); + Log::phase("converting LLVM intrinsic functions to standard functions"); convertLLVMIntrinsicFunctions(); if (resModule->isDebugInfoAvailable()) { - llvm_support::printPhase("obtaining debug information", Debug); + Log::phase("obtaining debug information"); obtainDebugInfo(); } if (!globalConfig->parameters.isBackendNoOpts()) { - llvm_support::printPhase( - "alias analysis [" + aliasAnalysis->getId() + "]", - Debug - ); + Log::phase("alias analysis [" + aliasAnalysis->getId() + "]"); initAliasAnalysis(); - llvm_support::printPhase( - "optimizations [" + getTypeOfRunOptimizations() + "]", - Debug - ); + Log::phase("optimizations [" + getTypeOfRunOptimizations() + "]"); runOptimizations(); } if (!globalConfig->parameters.isBackendNoVarRenaming()) { - llvm_support::printPhase( - "variable renaming [" + varRenamer->getId() + "]", - Debug - ); + Log::phase("variable renaming [" + varRenamer->getId() + "]"); renameVariables(); } if (!globalConfig->parameters.isBackendNoSymbolicNames()) { - llvm_support::printPhase( - "converting constants to symbolic names", - Debug - ); + Log::phase("converting constants to symbolic names"); convertConstantsToSymbolicNames(); } if (ValidateModule) { - llvm_support::printPhase("module validation", Debug); + Log::phase("module validation"); validateResultingModule(); } if (!FindPatterns.empty()) { - llvm_support::printPhase("finding patterns", Debug); + Log::phase("finding patterns"); findPatterns(); } if (globalConfig->parameters.isBackendEmitCfg()) { - llvm_support::printPhase("emission of control-flow graphs", Debug); + Log::phase("emission of control-flow graphs"); emitCFGs(); } if (globalConfig->parameters.isBackendEmitCg()) { - llvm_support::printPhase("emission of a call graph", Debug); + Log::phase("emission of a call graph"); emitCG(); } - llvm_support::printPhase( - "emission of the target code [" + hllWriter->getId() + "]", - Debug - ); + Log::phase("emission of the target code [" + hllWriter->getId() + "]"); emitTargetHLLCode(); - llvm_support::printPhase("finalization", Debug); + Log::phase("finalization"); finalize(); - llvm_support::printPhase("cleanup", Debug); + Log::phase("cleanup"); cleanup(); return false; @@ -293,9 +266,9 @@ bool LlvmIr2Hll::initialize(llvm::Module &m) // Instantiate the requested HLL writer and make sure it exists. We need to // explicitly specify template parameters because raw_pwrite_stream has // a private copy constructor, so it needs to be passed by reference. - llvm_support::printSubPhase( - "creating the used HLL writer [" + TargetHLL + "]", - Debug + Log::phase( + "creating the used HLL writer [" + TargetHLL + "]", + Log::SubPhase ); // Output stream into which the generated code will be emitted. @@ -325,9 +298,9 @@ bool LlvmIr2Hll::initialize(llvm::Module &m) } // Instantiate the requested alias analysis and make sure it exists. - llvm_support::printSubPhase( - "creating the used alias analysis [" + oAliasAnalysis + "]", - Debug + Log::phase( + "creating the used alias analysis [" + oAliasAnalysis + "]", + Log::SubPhase ); aliasAnalysis = llvmir2hll::AliasAnalysisFactory::getInstance().createObject( oAliasAnalysis @@ -342,10 +315,10 @@ bool LlvmIr2Hll::initialize(llvm::Module &m) // Instantiate the requested obtainer of information about function // calls and make sure it exists. - llvm_support::printSubPhase( - "creating the used call info obtainer [" - + globalConfig->parameters.getBackendCallInfoObtainer() + "]", - Debug + Log::phase( + "creating the used call info obtainer [" + + globalConfig->parameters.getBackendCallInfoObtainer() + "]", + Log::SubPhase ); cio = llvmir2hll::CallInfoObtainerFactory::getInstance().createObject( globalConfig->parameters.getBackendCallInfoObtainer() @@ -360,10 +333,10 @@ bool LlvmIr2Hll::initialize(llvm::Module &m) // Instantiate the requested evaluator of arithmetical expressions and make // sure it exists. - llvm_support::printSubPhase( - "creating the used evaluator of arithmetical expressions [" + - oArithmExprEvaluator + "]", - Debug + Log::phase( + "creating the used evaluator of arithmetical expressions [" + + oArithmExprEvaluator + "]", + Log::SubPhase ); auto& aeef = llvmir2hll::ArithmExprEvaluatorFactory::getInstance(); arithmExprEvaluator = aeef.createObject(oArithmExprEvaluator); @@ -378,9 +351,9 @@ bool LlvmIr2Hll::initialize(llvm::Module &m) // Instantiate the requested variable names generator and make sure it // exists. - llvm_support::printSubPhase( - "creating the used variable names generator [" + oVarNameGen + "]", - Debug + Log::phase( + "creating the used variable names generator [" + oVarNameGen + "]", + Log::SubPhase ); varNameGen = llvmir2hll::VarNameGenFactory::getInstance().createObject( oVarNameGen, @@ -395,10 +368,10 @@ bool LlvmIr2Hll::initialize(llvm::Module &m) } // Instantiate the requested variable renamer and make sure it exists. - llvm_support::printSubPhase( - "creating the used variable renamer [" - + globalConfig->parameters.getBackendVarRenamer() + "]", - Debug + Log::phase( + "creating the used variable renamer [" + + globalConfig->parameters.getBackendVarRenamer() + "]", + Log::SubPhase ); varRenamer = llvmir2hll::VarRenamerFactory::getInstance().createObject( globalConfig->parameters.getBackendVarRenamer(), @@ -445,18 +418,18 @@ void LlvmIr2Hll::createSemanticsFromParameter() if (oSemantics.empty() || oSemantics == "-") { // Do no use any semantics. - llvm_support::printSubPhase( - "creating the used semantics [none]", - Debug + Log::phase( + "creating the used semantics [none]", + Log::SubPhase ); semantics = llvmir2hll::DefaultSemantics::create(); } else { // Use the given semantics. - llvm_support::printSubPhase( - "creating the used semantics [" + oSemantics + "]", - Debug + Log::phase( + "creating the used semantics [" + oSemantics + "]", + Log::SubPhase ); semantics = llvmir2hll::CompoundSemanticsBuilder::build( split(oSemantics, ',') @@ -474,9 +447,9 @@ void LlvmIr2Hll::createSemanticsFromLLVMIR() std::string usedSemantics("libc,gcc-general,win-api"); // Use the list to create the semantics. - llvm_support::printSubPhase( - "creating the used semantics [" + usedSemantics + "]", - Debug + Log::phase( + "creating the used semantics [" + usedSemantics + "]", + Log::SubPhase ); semantics = llvmir2hll::CompoundSemanticsBuilder::build( split(usedSemantics, ',') @@ -493,12 +466,12 @@ bool LlvmIr2Hll::loadConfig() // Currently, we always use the JSON config. if (globalConfig == nullptr) { - llvm_support::printSubPhase("creating a new config", Debug); + Log::phase("creating a new config", Log::SubPhase); config = llvmir2hll::JSONConfig::empty(); return true; } - llvm_support::printSubPhase("loading the input config", Debug); + Log::phase("loading the input config", Log::SubPhase); try { config = llvmir2hll::JSONConfig::fromString( @@ -508,9 +481,9 @@ bool LlvmIr2Hll::loadConfig() } catch (const llvmir2hll::ConfigError &ex) { - llvm_support::printErrorMessage( - "Loading of the config failed: " + ex.getMessage() + "." - ); + Log::error() << Log::Error + << "Loading of the config failed: " << ex.getMessage() << "." + << std::endl; return false; } } @@ -570,7 +543,7 @@ void LlvmIr2Hll::removeLibraryFuncs() llvmir2hll::sortByName(removedFuncs); for (const auto &func : removedFuncs) { - llvm_support::printSubPhase("removing " + func->getName() + "()"); + Log::phase("removing " + func->getName() + "()"); } } } @@ -667,7 +640,7 @@ void LlvmIr2Hll::validateResultingModule() std::sort(regValidatorIDs.begin(), regValidatorIDs.end()); for (const auto &id : regValidatorIDs) { - llvm_support::printSubPhase("running " + id + "Validator", Debug); + Log::phase("running " + id + "Validator", Log::SubPhase); ShPtr validator( llvmir2hll::ValidatorFactory::getInstance().createObject(id) ); @@ -738,9 +711,10 @@ void LlvmIr2Hll::emitCFGs() { if (globalConfig->parameters.getOutputFile().empty()) { - llvm_support::printErrorMessage( - "Output file not set, cannot generate output CFG files." - ); + Log::error() << Log::Error + << "Output file not set, cannot generate output CFG files." + << std::endl; + return; } @@ -777,9 +751,9 @@ void LlvmIr2Hll::emitCFGs() std::ofstream out(fileName.c_str()); if (!out) { - llvm_support::printErrorMessage( - "Cannot open " + fileName + " for writing." - ); + Log::error() << Log::Error + << "Cannot open " + fileName + " for writing." + << std::endl; return; } // Create a CFG for the current function and emit it into the opened @@ -808,9 +782,9 @@ void LlvmIr2Hll::emitCG() { if (globalConfig->parameters.getOutputFile().empty()) { - llvm_support::printErrorMessage( - "Output file not set, cannot generate output CG file." - ); + Log::error() << Log::Error + << "Output file not set, cannot generate output CG file." + << std::endl; return; } @@ -838,9 +812,9 @@ void LlvmIr2Hll::emitCG() std::ofstream out(fileName.c_str()); if (!out) { - llvm_support::printErrorMessage( - "Cannot open " + fileName + " for writing." - ); + Log::error() << Log::Error + << "Cannot open " + fileName + " for writing." + << std::endl; return; } @@ -925,8 +899,9 @@ LlvmIr2Hll::instantiatePatternFinders( ); if (!pf && Debug) { - llvm_support::printWarningMessage( - "the requested pattern finder '" + pfId + "' does not exist"); + Log::error() << Log::Warning + << "the requested pattern finder '" + pfId + "' does not exist" + << std::endl; } else { @@ -945,7 +920,7 @@ LlvmIr2Hll::instantiatePatternFinderRunner() const if (Debug) { return ShPtr( - new llvmir2hll::CLIPatternFinderRunner(llvm::errs())); + new llvmir2hll::CLIPatternFinderRunner(Log::get(Log::Type::Error))); } return ShPtr( new llvmir2hll::NoActionPatternFinderRunner() diff --git a/src/llvmir2hll/obtainer/call_info_obtainer.cpp b/src/llvmir2hll/obtainer/call_info_obtainer.cpp index 3df40680e..9c9c08071 100644 --- a/src/llvmir2hll/obtainer/call_info_obtainer.cpp +++ b/src/llvmir2hll/obtainer/call_info_obtainer.cpp @@ -14,11 +14,10 @@ #include "retdec/llvmir2hll/ir/variable.h" #include "retdec/llvmir2hll/obtainer/call_info_obtainer.h" #include "retdec/llvmir2hll/support/debug.h" -#include "retdec/llvm-support/diagnostics.h" #include "retdec/utils/container.h" +#include "retdec/utils/io/log.h" -using namespace retdec::llvm_support; - +using namespace retdec::utils::io; using retdec::utils::hasItem; using retdec::utils::setDifference; using retdec::utils::setUnion; @@ -256,7 +255,7 @@ CallInfoObtainer::SCCWithRepresent CallInfoObtainer::findNextSCC( } // TODO Can this happen? - printWarningMessage("[SCCComputer] No viable SCC has been found."); + Log::error() << Log::Warning << "[SCCComputer] No viable SCC has been found." << std::endl; FuncSet scc; ShPtr func(*(remainingFuncs.begin())); scc.insert(func); diff --git a/src/llvmir2hll/obtainer/call_info_obtainers/optim_call_info_obtainer.cpp b/src/llvmir2hll/obtainer/call_info_obtainers/optim_call_info_obtainer.cpp index 36b1983b1..b2142fae8 100644 --- a/src/llvmir2hll/obtainer/call_info_obtainers/optim_call_info_obtainer.cpp +++ b/src/llvmir2hll/obtainer/call_info_obtainers/optim_call_info_obtainer.cpp @@ -13,12 +13,14 @@ #include "retdec/llvmir2hll/ir/variable.h" #include "retdec/llvmir2hll/obtainer/call_info_obtainer_factory.h" #include "retdec/llvmir2hll/obtainer/call_info_obtainers/optim_call_info_obtainer.h" -#include "retdec/llvmir2hll/support/debug.h" #include "retdec/utils/container.h" +#include "retdec/llvmir2hll/support/debug.h" +#include "retdec/utils/io/log.h" using retdec::utils::addToSet; using retdec::utils::hasItem; using retdec::utils::setIntersection; +using namespace retdec::utils::io; namespace retdec { namespace llvmir2hll { @@ -38,33 +40,33 @@ OptimCallInfo::OptimCallInfo(ShPtr call): CallInfo(call) {} * Only for debugging purposes. */ void OptimCallInfo::debugPrint() { - llvm::errs() << "[OptimCallInfo] Debug info for '" << call << "':\n"; + Log::error() << "[OptimCallInfo] Debug info for '" << call << "':\n"; - llvm::errs() << " neverReadVars: "; + Log::error() << " neverReadVars: "; dump(neverReadVars, dumpFuncGetName>); - llvm::errs() << " mayBeReadVars: "; + Log::error() << " mayBeReadVars: "; dump(mayBeReadVars, dumpFuncGetName>); - llvm::errs() << " alwaysReadVars: "; + Log::error() << " alwaysReadVars: "; dump(alwaysReadVars, dumpFuncGetName>); - llvm::errs() << " neverModifiedVars: "; + Log::error() << " neverModifiedVars: "; dump(neverModifiedVars, dumpFuncGetName>); - llvm::errs() << " mayBeModifiedVars: "; + Log::error() << " mayBeModifiedVars: "; dump(mayBeModifiedVars, dumpFuncGetName>); - llvm::errs() << " alwaysModifiedVars: "; + Log::error() << " alwaysModifiedVars: "; dump(alwaysModifiedVars, dumpFuncGetName>); - llvm::errs() << " varsWithNeverChangedValue: "; + Log::error() << " varsWithNeverChangedValue: "; dump(varsWithNeverChangedValue, dumpFuncGetName>); - llvm::errs() << " varsAlwaysModifiedBeforeRead: "; + Log::error() << " varsAlwaysModifiedBeforeRead: "; dump(varsAlwaysModifiedBeforeRead, dumpFuncGetName>); - llvm::errs() << "\n"; + Log::error() << "\n"; } bool OptimCallInfo::isNeverRead(ShPtr var) const { @@ -111,33 +113,33 @@ OptimFuncInfo::OptimFuncInfo(ShPtr func): FuncInfo(func) {} * Only for debugging purposes. */ void OptimFuncInfo::debugPrint() { - llvm::errs() << "[OptimFuncInfo] Debug info for function '" << func->getName() << "':\n"; + Log::error() << "[OptimFuncInfo] Debug info for function '" << func->getName() << "':\n"; - llvm::errs() << " neverReadVars: "; + Log::error() << " neverReadVars: "; dump(neverReadVars, dumpFuncGetName>); - llvm::errs() << " mayBeReadVars: "; + Log::error() << " mayBeReadVars: "; dump(mayBeReadVars, dumpFuncGetName>); - llvm::errs() << " alwaysReadVars: "; + Log::error() << " alwaysReadVars: "; dump(alwaysReadVars, dumpFuncGetName>); - llvm::errs() << " neverModifiedVars: "; + Log::error() << " neverModifiedVars: "; dump(neverModifiedVars, dumpFuncGetName>); - llvm::errs() << " mayBeModifiedVars: "; + Log::error() << " mayBeModifiedVars: "; dump(mayBeModifiedVars, dumpFuncGetName>); - llvm::errs() << " alwaysModifiedVars: "; + Log::error() << " alwaysModifiedVars: "; dump(alwaysModifiedVars, dumpFuncGetName>); - llvm::errs() << " varsWithNeverChangedValue: "; + Log::error() << " varsWithNeverChangedValue: "; dump(varsWithNeverChangedValue, dumpFuncGetName>); - llvm::errs() << " varsAlwaysModifiedBeforeRead: "; + Log::error() << " varsAlwaysModifiedBeforeRead: "; dump(varsAlwaysModifiedBeforeRead, dumpFuncGetName>); - llvm::errs() << "\n"; + Log::error() << "\n"; } bool OptimFuncInfo::isNeverRead(ShPtr var) const { diff --git a/src/llvmir2hll/optimizer/optimizer_manager.cpp b/src/llvmir2hll/optimizer/optimizer_manager.cpp index 0dce37b8d..806fd6f02 100644 --- a/src/llvmir2hll/optimizer/optimizer_manager.cpp +++ b/src/llvmir2hll/optimizer/optimizer_manager.cpp @@ -50,8 +50,9 @@ #include "retdec/utils/container.h" #include "retdec/utils/string.h" #include "retdec/utils/system.h" +#include "retdec/utils/io/log.h" -using namespace retdec::llvm_support; +using namespace retdec::utils::io; using namespace std::string_literals; using retdec::utils::hasItem; @@ -309,7 +310,7 @@ void OptimizerManager::runOptimizerProvidedItShouldBeRun(ShPtr optimi try { optimizer->optimize(); } catch (const std::bad_alloc &) { - printWarningMessage("out of memory; trying to recover"); + Log::error() << Log::Warning << "out of memory; trying to recover" << std::endl; std::this_thread::sleep_for(std::chrono::seconds(1)); } } else { @@ -328,7 +329,7 @@ void OptimizerManager::runOptimizerProvidedItShouldBeRun(ShPtr optimi */ void OptimizerManager::printOptimization(const std::string &optId) const { if (enableDebug) { - printSubPhase("running "s + optId + OPT_SUFFIX); + Log::phase("running "s + optId + OPT_SUFFIX, Log::SubPhase); } } diff --git a/src/llvmir2hll/pattern/pattern_finder_runners/cli_pattern_finder_runner.cpp b/src/llvmir2hll/pattern/pattern_finder_runners/cli_pattern_finder_runner.cpp index 98be961dd..d4099bee1 100644 --- a/src/llvmir2hll/pattern/pattern_finder_runners/cli_pattern_finder_runner.cpp +++ b/src/llvmir2hll/pattern/pattern_finder_runners/cli_pattern_finder_runner.cpp @@ -9,9 +9,8 @@ #include "retdec/llvmir2hll/ir/statement.h" #include "retdec/llvmir2hll/pattern/pattern_finder_runners/cli_pattern_finder_runner.h" #include "retdec/llvmir2hll/support/debug.h" -#include "retdec/llvm-support/diagnostics.h" -using namespace retdec::llvm_support; +using namespace retdec::utils::io; namespace retdec { namespace llvmir2hll { @@ -28,7 +27,7 @@ const std::string PATTERN_INFO_INDENT = " "; * * @param[out] os Output stream, into which the patterns will be emited. */ -CLIPatternFinderRunner::CLIPatternFinderRunner(llvm::raw_ostream &os): +CLIPatternFinderRunner::CLIPatternFinderRunner(utils::io::Logger &os): os(os) {} /** @@ -38,7 +37,7 @@ CLIPatternFinderRunner::CLIPatternFinderRunner(llvm::raw_ostream &os): */ void CLIPatternFinderRunner::doActionsBeforePatternFinderRuns( ShPtr pf) { - printSubPhase("running " + pf->getId() + "PatternFinder", true, os); + os << "running " + pf->getId() + "PatternFinder"; } /** @@ -55,8 +54,11 @@ void CLIPatternFinderRunner::doActionsAfterPatternFinderHasRun( * @brief Prints information about the given pattern. */ void CLIPatternFinderRunner::printPatternInfo(const ShPtr &p) { - os << PATTERN_INFO_INDENT << "Found pattern:" << "\n"; - p->print(os, PATTERN_INFO_INDENT + " "); + os << PATTERN_INFO_INDENT << "Found pattern:" << std::endl; + std::string str; + llvm::raw_string_ostream ss(str); + p->print(ss); + os << PATTERN_INFO_INDENT << " " << str; } } // namespace llvmir2hll diff --git a/src/llvmir2hll/pattern/pattern_finders/api_call_seq_pattern_finder.cpp b/src/llvmir2hll/pattern/pattern_finders/api_call_seq_pattern_finder.cpp index 3c2e20de8..513ceeb43 100644 --- a/src/llvmir2hll/pattern/pattern_finders/api_call_seq_pattern_finder.cpp +++ b/src/llvmir2hll/pattern/pattern_finders/api_call_seq_pattern_finder.cpp @@ -25,9 +25,9 @@ #include "retdec/llvmir2hll/pattern/patterns/stmts_pattern.h" #include "retdec/llvmir2hll/support/debug.h" #include "retdec/llvmir2hll/utils/ir.h" -#include "retdec/llvm-support/diagnostics.h" +#include "retdec/utils/io/log.h" -using namespace retdec::llvm_support; +using namespace retdec::utils::io; namespace retdec { namespace llvmir2hll { @@ -57,9 +57,9 @@ void parseAndAddAPICallInfoSeqToMap(APICallInfoSeqMap &map, if (seq) { map.insert(std::make_pair(funcName, seq.value())); } else { - printErrorMessage( - "APICallInfoSeqParser failed to parse the following pattern: ", - seqTextRepr); + Log::error() << Log::Error + << "APICallInfoSeqParser failed to parse the following pattern: " + << seqTextRepr; } } diff --git a/src/llvmir2hll/retdec-llvmir2hll-config.cmake b/src/llvmir2hll/retdec-llvmir2hll-config.cmake index 8e751efb4..b3b389462 100644 --- a/src/llvmir2hll/retdec-llvmir2hll-config.cmake +++ b/src/llvmir2hll/retdec-llvmir2hll-config.cmake @@ -5,7 +5,6 @@ if(NOT TARGET retdec::llvmir2hll) COMPONENTS config utils - llvm-support rapidjson llvm ) diff --git a/src/llvmir2hll/semantics/semantics/compound_semantics_builder.cpp b/src/llvmir2hll/semantics/semantics/compound_semantics_builder.cpp index a6226b5e3..76658b784 100644 --- a/src/llvmir2hll/semantics/semantics/compound_semantics_builder.cpp +++ b/src/llvmir2hll/semantics/semantics/compound_semantics_builder.cpp @@ -8,9 +8,9 @@ #include "retdec/llvmir2hll/semantics/semantics/compound_semantics_builder.h" #include "retdec/llvmir2hll/semantics/semantics_factory.h" #include "retdec/llvmir2hll/support/debug.h" -#include "retdec/llvm-support/diagnostics.h" +#include "retdec/utils/io/log.h" -using namespace retdec::llvm_support; +using namespace retdec::utils::io; namespace retdec { namespace llvmir2hll { @@ -38,8 +38,9 @@ ShPtr CompoundSemanticsBuilder::build( if (semantics) { compoundSemantics->appendSemantics(semantics); } else { - printWarningMessage("There is no registered semantics with ID \"", - id, "\", skipping."); + Log::error() << Log::Warning + << "There is no registered semantics with ID \"" << id << "\", skipping." + << std::endl; } } diff --git a/src/llvmir2hll/validator/validators/break_outside_loop_validator.cpp b/src/llvmir2hll/validator/validators/break_outside_loop_validator.cpp index ac6502ea7..6e32dc8fc 100644 --- a/src/llvmir2hll/validator/validators/break_outside_loop_validator.cpp +++ b/src/llvmir2hll/validator/validators/break_outside_loop_validator.cpp @@ -35,8 +35,10 @@ void BreakOutsideLoopValidator::visit(ShPtr stmt) { // this end, get the innermost loop or switch. ShPtr innLoopOrSwitch(getInnermostLoopOrSwitch(stmt)); if (!innLoopOrSwitch) { - validationError("In ", func->getName(), "(), found `", stmt, - "` outside of a loop or a switch statement."); + std::ostringstream stmtStr; + stmtStr << stmt; + validationError("In " + func->getName() + "(), found `" + stmtStr.str() + + "` outside of a loop or a switch statement."); } OrderedAllVisitor::visit(stmt); } @@ -46,8 +48,10 @@ void BreakOutsideLoopValidator::visit(ShPtr stmt) { // innermost loop. ShPtr innLoop(getInnermostLoop(stmt)); if (!innLoop) { - validationError("In ", func->getName(), "(), found `", stmt, - "` outside of a loop."); + std::ostringstream stmtStr; + stmtStr << stmt; + validationError("In " + func->getName() + "(), found `" + stmtStr.str() + +"` outside of a loop."); } OrderedAllVisitor::visit(stmt); } diff --git a/src/llvmir2hll/validator/validators/no_global_var_def_validator.cpp b/src/llvmir2hll/validator/validators/no_global_var_def_validator.cpp index 116fa5544..4a22fae8a 100644 --- a/src/llvmir2hll/validator/validators/no_global_var_def_validator.cpp +++ b/src/llvmir2hll/validator/validators/no_global_var_def_validator.cpp @@ -35,9 +35,11 @@ std::string NoGlobalVarDefValidator::getId() const { void NoGlobalVarDefValidator::visit(ShPtr stmt) { // The left-hand side of a VarDefStmt cannot be a global variable. + std::ostringstream stmtStr; + stmtStr << stmt; if (module->isGlobalVar(stmt->getVar())) { - validationError("In ", func->getName(), "(), found a VarDefStmt `", - stmt, "` that defines a global variable."); + validationError("In "+func->getName()+"(), found a VarDefStmt `"+ + stmtStr.str()+"` that defines a global variable."); } OrderedAllVisitor::visit(stmt); } diff --git a/src/llvmir2hll/validator/validators/return_validator.cpp b/src/llvmir2hll/validator/validators/return_validator.cpp index d403d35a6..2d1384f08 100644 --- a/src/llvmir2hll/validator/validators/return_validator.cpp +++ b/src/llvmir2hll/validator/validators/return_validator.cpp @@ -37,14 +37,18 @@ std::string ReturnValidator::getId() const { void ReturnValidator::visit(ShPtr stmt) { // If the function is non-void, there has to be a return value. if (!isa(func->getRetType()) && !stmt->getRetVal()) { - validationError("In ", func->getName(), "(), which is non-void, ", - "found a ReturnStmt `", stmt, "` without a return value."); + std::ostringstream stmtStr; + stmtStr << stmt; + validationError("In "+func->getName()+"(), which is non-void, " + "found a ReturnStmt `"+stmtStr.str()+"` without a return value."); } // If the function is void, there cannot be a return value. if (isa(func->getRetType()) && stmt->getRetVal()) { - validationError("In ", func->getName(), "(), which returns void, ", - "found a ReturnStmt `", stmt, "` with a return value."); + std::ostringstream stmtStr; + stmtStr << stmt; + validationError("In "+func->getName()+"(), which returns void, " + "found a ReturnStmt `"+stmtStr.str()+"` with a return value."); } OrderedAllVisitor::visit(stmt); diff --git a/src/macho-extractor/break_fat.cpp b/src/macho-extractor/break_fat.cpp index 302569f0d..3988b999e 100644 --- a/src/macho-extractor/break_fat.cpp +++ b/src/macho-extractor/break_fat.cpp @@ -4,7 +4,6 @@ * @copyright (c) 2017 Avast Software, licensed under the MIT license */ -#include #include #include @@ -13,6 +12,7 @@ #include "retdec/macho-extractor/break_fat.h" #include "retdec/utils/conversion.h" +#include "retdec/utils/io/log.h" #include "retdec/utils/string.h" using namespace llvm; @@ -21,6 +21,7 @@ using namespace llvm::object; using namespace llvm::sys; using namespace rapidjson; using namespace retdec::utils; +using namespace retdec::utils::io; namespace { @@ -337,7 +338,7 @@ bool BreakMachOUniversal::listArchitectures( // Write warning when --object option is used on non-archive target. if(!isStatic && withObjects) { - std::cerr << "Warning: input file is not an archive! (--objects)\n"; + Log::error() << Log::Warning << "input file is not an archive! (--objects)\n"; } return output.good(); diff --git a/src/macho-extractortool/macho_extractor.cpp b/src/macho-extractortool/macho_extractor.cpp index 1cd39ee2a..32f88a4e3 100644 --- a/src/macho-extractortool/macho_extractor.cpp +++ b/src/macho-extractortool/macho_extractor.cpp @@ -4,7 +4,6 @@ * @copyright (c) 2017 Avast Software, licensed under the MIT license */ -#include #include #include @@ -13,8 +12,10 @@ #include "retdec/utils/conversion.h" #include "retdec/utils/string.h" +#include "retdec/utils/io/log.h" #include "retdec/macho-extractor/break_fat.h" +using namespace retdec::utils::io; using namespace retdec::macho_extractor; using namespace rapidjson; @@ -24,7 +25,7 @@ enum class Mode { All, Best, Arch, Family, Index }; void printUsage() { - std::cerr << + Log::error() << "\nExtract objects from Mach-O Universal Binaries.\n" "Usage: retdec-macho-extractor [OPTIONS] FILE\n\n" "Extraction options:\n\n" @@ -73,7 +74,7 @@ std::string getParamOrDie( } else { - std::cerr << "Error: missing argument value.\n\n"; + Log::error() << Log::Error << "missing argument value.\n\n"; printUsage(); exit(1); } @@ -98,11 +99,11 @@ void printError( StringBuffer outBuffer; PrettyWriter outWriter(outBuffer); outDoc.Accept(outWriter); - std::cout << outBuffer.GetString(); + Log::info() << outBuffer.GetString(); } else { - std::cerr << "Error: " << message << ".\n"; + Log::error() << Log::Error << message << ".\n"; } } @@ -117,7 +118,7 @@ int handleArguments( if(args.size() < 1) { printUsage(); - std::cerr << "Error: not enough arguments!\n"; + Log::error() << Log::Error << "not enough arguments!\n"; return 1; } @@ -254,25 +255,31 @@ int handleArguments( { if(binary.isStaticLibrary()) { - std::cout << "Input file is a static library.\n"; + Log::info() << "Input file is a static library.\n"; return 0; } - std::cout << "Input file is NOT a static library.\n"; + Log::info() << "Input file is NOT a static library.\n"; return 3; } // List mode if(listOnly) { + std::stringstream ss; + bool ret; + if(jsonOut) { - return binary.listArchitecturesJson(std::cout, addObjects) ? 0 : 1; + ret = binary.listArchitecturesJson(ss, addObjects) ? 0 : 1; } else { - return binary.listArchitectures(std::cout, addObjects) ? 0 : 1; + ret = binary.listArchitectures(ss, addObjects) ? 0 : 1; } + + Log::info() << ss.str(); + return ret; } // Set default name if no name was given diff --git a/src/pat2yara/pat2yara.cpp b/src/pat2yara/pat2yara.cpp index 3b792892b..7b0b89e6c 100644 --- a/src/pat2yara/pat2yara.cpp +++ b/src/pat2yara/pat2yara.cpp @@ -5,10 +5,10 @@ */ #include -#include #include #include "retdec/utils/filesystem.h" +#include "retdec/utils/io/log.h" #include "pat2yara/processing.h" #include "yaramod/builder/yara_file_builder.h" #include "yaramod/builder/yara_rule_builder.h" @@ -18,17 +18,17 @@ * Application for further processing of raw yara rules from bin2pat. */ +using namespace retdec::utils::io; using namespace yaramod; /** * Print application usage. * - * @param outputStream stream to write usage to + * @param log logger object to write usage with */ -void printUsage( - std::ostream &outputStream) +void printUsage(Logger& log) { - outputStream << + log << "Usage: pat2yara [-o OUTPUT_FILE] [--max-size VALUE] [--min-size VALUE]\n" " [--min-pure VALUE] [-o OUTPUT_FILE] INPUT_FILE [INPUT_FILE...]\n\n" "-o --output OUTPUT_FILE\n" @@ -60,7 +60,7 @@ void printUsage( int dieWithError( const std::string &message) { - std::cerr << "Error: " << message << "\n"; + Log::error() << Log::Error << message << "\n"; return 1; } @@ -72,7 +72,7 @@ int dieWithError( void printWarning( const std::string &message) { - std::cerr << "Warning: " << message << "\n"; + Log::error() << Log::Warning << message << "\n"; } /** @@ -113,7 +113,7 @@ int processArguments(std::vector &args) for (std::size_t i = 0; i < args.size(); ++i) { if (args[i] == "--help" || args[i] == "-h") { - printUsage(std::cout); + printUsage(Log::get(Log::Type::Info)); return 0; } else if (args[i] == "--delphi") { @@ -190,7 +190,7 @@ int processArguments(std::vector &args) } else { processFiles(fileBuilder, logBuilder, options); - std::cout << fileBuilder.get(false)->getText() << std::endl; + Log::info() << fileBuilder.get(false)->getText() << std::endl; } // Write log-file. diff --git a/src/pdbparser/pdb_types.cpp b/src/pdbparser/pdb_types.cpp index 54cec734c..bf10954f3 100644 --- a/src/pdbparser/pdb_types.cpp +++ b/src/pdbparser/pdb_types.cpp @@ -9,7 +9,6 @@ #include #include #include -#include #include #include "retdec/pdbparser/pdb_types.h" diff --git a/src/pelib/ResourceDirectory.cpp b/src/pelib/ResourceDirectory.cpp index 7e8d9b5e6..cec9d7a8c 100644 --- a/src/pelib/ResourceDirectory.cpp +++ b/src/pelib/ResourceDirectory.cpp @@ -355,7 +355,7 @@ namespace PeLib **/ void ResourceLeaf::rebuild(OutputBuffer& obBuffer, unsigned int uiOffset, unsigned int uiRva, const std::string&) const { -// std::cout << std::hex << pad << "Leaf: " << uiOffset << std::endl; +// Log::debug() << std::hex << pad << "Leaf: " << uiOffset << std::endl; // obBuffer << entry.OffsetToData; // obBuffer << uiOffset; @@ -374,7 +374,7 @@ namespace PeLib obBuffer.insert(entry.OffsetToData - uiRva + i, m_data[i]); } -// std::cout << "LeafChild: " << std::endl; +// Log::debug() << "LeafChild: " << std::endl; } /** @@ -537,20 +537,20 @@ namespace PeLib **/ void ResourceNode::rebuild(OutputBuffer& obBuffer, unsigned int uiOffset, unsigned int uiRva, const std::string& pad) const { -/* std::cout << std::hex << pad << uiOffset << std::endl; +/* Log::debug() << std::hex << pad << uiOffset << std::endl; - std::cout << std::hex << pad << "header.Characteristics: " << header.Characteristics << std::endl; - std::cout << std::hex << pad << "header.TimeDateStamp: " << header.TimeDateStamp << std::endl; - std::cout << std::hex << pad << "header.MajorVersion: " << header.MajorVersion << std::endl; - std::cout << std::hex << pad << "header.MinorVersion: " << header.MinorVersion << std::endl; - std::cout << std::hex << pad << "header.NumberOfNamedEntries: " << header.NumberOfNamedEntries << std::endl; - std::cout << std::hex << pad << "header.NumberOfIdEntries: " << header.NumberOfIdEntries << std::endl; + Log::debug() << std::hex << pad << "header.Characteristics: " << header.Characteristics << std::endl; + Log::debug() << std::hex << pad << "header.TimeDateStamp: " << header.TimeDateStamp << std::endl; + Log::debug() << std::hex << pad << "header.MajorVersion: " << header.MajorVersion << std::endl; + Log::debug() << std::hex << pad << "header.MinorVersion: " << header.MinorVersion << std::endl; + Log::debug() << std::hex << pad << "header.NumberOfNamedEntries: " << header.NumberOfNamedEntries << std::endl; + Log::debug() << std::hex << pad << "header.NumberOfIdEntries: " << header.NumberOfIdEntries << std::endl; */ obBuffer.insert(uiOffset, header.Characteristics); obBuffer.insert(uiOffset + 4, header.TimeDateStamp); obBuffer.insert(uiOffset + 8, header.MajorVersion); obBuffer.insert(uiOffset + 10, header.MinorVersion); - //std::cout << pad << "Children: " << children.size() << std::endl; + //Log::debug() << pad << "Children: " << children.size() << std::endl; obBuffer.insert(uiOffset + 12, header.NumberOfNamedEntries); obBuffer.insert(uiOffset + 14, header.NumberOfIdEntries); @@ -971,7 +971,7 @@ namespace PeLib { if (children.size()) { - std::cout << std::accumulate(children.begin(), children.end(), 0, accumulate) << std::endl; + Log::debug() << std::accumulate(children.begin(), children.end(), 0, accumulate) << std::endl; return PELIB_IMAGE_RESOURCE_DIRECTORY::size() + std::accumulate(children.begin(), children.end(), 0, accumulate); } @@ -1072,7 +1072,7 @@ namespace PeLib { OutputBuffer obBuffer(vBuffer); unsigned int offs = 0; -// std::cout << "Root: " << m_rnRoot.children.size() << std::endl; +// Log::debug() << "Root: " << m_rnRoot.children.size() << std::endl; m_rnRoot.rebuild(obBuffer, offs, uiRva, ""); } @@ -1434,10 +1434,10 @@ namespace PeLib unsigned int ResourceDirectory::getNumberOfResources(std::uint32_t dwId) const { // std::vector::const_iterator IterD = m_rnRoot.children.begin(); -// std::cout << dwId << std::endl; +// Log::debug() << dwId << std::endl; // while (IterD != m_rnRoot.children.end()) // { -// std::cout << IterD->entry.irde.Name << std::endl; +// Log::debug() << IterD->entry.irde.Name << std::endl; // ++IterD; // } diff --git a/src/retdec-decompiler/retdec-decompiler.cpp b/src/retdec-decompiler/retdec-decompiler.cpp index 64dd29de6..97336e978 100644 --- a/src/retdec-decompiler/retdec-decompiler.cpp +++ b/src/retdec-decompiler/retdec-decompiler.cpp @@ -4,7 +4,6 @@ * @copyright (c) 2020 Avast Software, licensed under the MIT license */ -#include #include #include #include @@ -40,7 +39,6 @@ #include "retdec/ar-extractor/archive_wrapper.h" #include "retdec/ar-extractor/detection.h" #include "retdec/config/config.h" -#include "retdec/llvm-support/diagnostics.h" #include "retdec/retdec/retdec.h" #include "retdec/macho-extractor/break_fat.h" #include "retdec/unpackertool/unpackertool.h" @@ -49,6 +47,10 @@ #include "retdec/utils/string.h" #include "retdec/utils/memory.h" +#include "retdec/utils/io/log.h" + +using namespace retdec::utils::io; + const int EXIT_TIMEOUT = 137; const int EXIT_BAD_ALLOC = 135; @@ -512,6 +514,10 @@ void ProgramOptions::loadOption(std::list::iterator& i) ); } } + else if (isParam(i, "-s", "--silent")) + { + params.setIsVerboseOutput(false); + } // Input file is the only argument that does not have -x or --xyz // before it. But only one input is expected. else if (params.getInputFile().empty()) @@ -605,11 +611,12 @@ std::string ProgramOptions::checkFile( void ProgramOptions::printHelpAndDie() { - std::cout << programName << R"(: + Log::info() << programName << R"(: Mandatory arguments: INPUT_FILE File to decompile. General arguments: [-o|--output FILE] Output file (default: INPUT_FILE.c if OUTPUT_FORMAT is plain, INPUT_FILE.c.json if OUTPUT_FORMAT is json|json-human). + [-s|--silent] Turns off informative output of the decompilation. [-f|--output-format OUTPUT_FORMAT] Output format [plain|json|json-human] (default: plain). [-m|--mode MODE] Force the type of decompilation mode [bin|raw] (default: bin). [-p|--pdb FILE] File with PDB debug information. @@ -750,8 +757,42 @@ void limitMaximalMemoryIfRequested(const retdec::config::Parameters& params) //============================================================================== // +/** + * TODO: this function is exact copy of the function located in retdec/retdec.cpp. + * The reason for this is that right now creation of correct interface that + * would hold this function is much more time expensive than hard copy. + * + * This function should be located in utils/io/log.{cpp,h}. For that it should + * now retdec::config::Parameters object. Inclusion of this object would be, + * however, only possible after linking rapidjson library to the retdec::utils. + * This is not wanted. Best solution would be making Parameters unaware of + * rapidjson. + */ +void setLogsFrom(const retdec::config::Parameters& params) +{ + auto logFile = params.getLogFile(); + auto errFile = params.getErrFile(); + auto verbose = params.isVerboseOutput(); + + Logger::Ptr outLog = nullptr; + + outLog.reset( + logFile.empty() + ? new Logger(std::cout, verbose) + : new FileLogger(logFile, verbose) + ); + + Log::set(Log::Type::Info, std::move(outLog)); + + if (!errFile.empty()) { + Log::set(Log::Type::Error, Logger::Ptr(new FileLogger(errFile))); + } +} + int decompile(retdec::config::Config& config, ProgramOptions& po) { + setLogsFrom(config.parameters); + // Macho-O extraction. // retdec::macho_extractor::BreakMachOUniversal fat( @@ -759,7 +800,7 @@ int decompile(retdec::config::Config& config, ProgramOptions& po) ); if (fat.isValid()) { - retdec::llvm_support::printPhase("Mach-O extraction"); + Log::phase("Mach-O extraction"); auto extractedFile = po.arExtractPath + "_m"; @@ -797,7 +838,7 @@ int decompile(retdec::config::Config& config, ProgramOptions& po) // if (po.arIdx || !po.arName.empty()) { - retdec::llvm_support::printPhase("Archive extraction"); + Log::phase("Archive extraction"); bool ok = true; std::string errMsg; @@ -855,39 +896,40 @@ int decompile(retdec::config::Config& config, ProgramOptions& po) ); if (ok && arw.isThinArchive()) { - std::cerr << "This file is an archive!" << std::endl; - std::cerr << "Error: File is a thin archive and cannot be decompiled." << std::endl; + Log::error() << "This file is an archive!" << std::endl; + Log::error() << "Error: File is a thin archive and cannot be decompiled." << std::endl; return EXIT_FAILURE; } else if (ok && arw.isEmptyArchive()) { - std::cerr << "This file is an archive!" << std::endl; - std::cerr << "Error: The input archive is empty." << std::endl; + Log::error() << "This file is an archive!" << std::endl; + Log::error() << "Error: The input archive is empty." << std::endl; return EXIT_FAILURE; } else if (ok) { - std::cerr << "This file is an archive!" << std::endl; + Log::error() << "This file is an archive!" << std::endl; std::string result; if (arw.getPlainTextList(result, errMsg, false, true)) { - std::cerr << result << std::endl; + Log::error() << result << std::endl; } return EXIT_FAILURE; } if (!ok && retdec::ar_extractor::isArchive(config.parameters.getInputFile())) { - std::cerr << "This file is an archive!" << std::endl; - std::cerr << "Error: The input archive has invalid format." << std::endl; + Log::error() << "This file is an archive!" << std::endl; + Log::error() << "Error: The input archive has invalid format." << std::endl; return EXIT_FAILURE; } } // Unpacking // - retdec::llvm_support::printPhase("Unpacking"); + + Log::phase("Unpacking"); std::vector unpackArgs; unpackArgs.push_back("whatever_program_name"); unpackArgs.push_back(config.parameters.getInputFile()); @@ -970,7 +1012,7 @@ int main(int argc, char **argv) } catch (const std::runtime_error& e) { - std::cerr << "Error: " << e.what() << std::endl; + Log::error() << Log::Error << e.what() << std::endl; return EXIT_FAILURE; } @@ -1001,7 +1043,7 @@ int main(int argc, char **argv) else { thr.detach(); // we leave the thread still running - std::cerr << "timeout after: " << config.parameters.getTimeout() + Log::error() << "timeout after: " << config.parameters.getTimeout() << " seconds" << std::endl; ret = EXIT_TIMEOUT; } @@ -1013,12 +1055,12 @@ int main(int argc, char **argv) } catch (const std::runtime_error& e) { - std::cerr << "Error: " << e.what() << std::endl; + Log::error() << Log::Error << e.what() << std::endl; ret = EXIT_FAILURE; } catch (const std::bad_alloc& e) { - std::cerr << "catched std::bad_alloc" << std::endl; + Log::error() << "catched std::bad_alloc" << std::endl; ret = EXIT_BAD_ALLOC; } diff --git a/src/retdec/retdec.cpp b/src/retdec/retdec.cpp index 8fb91962b..89b38f3f1 100644 --- a/src/retdec/retdec.cpp +++ b/src/retdec/retdec.cpp @@ -4,8 +4,6 @@ * @copyright (c) 2019 Avast Software, licensed under the MIT license */ -#include - #include #include #include @@ -50,9 +48,11 @@ #include "retdec/llvmir2hll/llvmir2hll.h" #include "retdec/config/config.h" -#include "retdec/llvm-support/diagnostics.h" #include "retdec/retdec/retdec.h" #include "retdec/utils/memory.h" +#include "retdec/utils/io/log.h" + +using namespace retdec::utils::io; // extern llvm::cl::opt PrintAfterAll; @@ -376,7 +376,7 @@ class ModulePassPrinter : public ModulePass { if (utils::startsWith(PhaseArg, "retdec")) { - llvm_support::printPhase(PhaseName); + Log::phase(PhaseName); LastPhase = PhaseArg; } else @@ -384,12 +384,12 @@ class ModulePassPrinter : public ModulePass // aggregate LLVM if (LastPhase != LlvmAggregatePhaseName) { - llvm_support::printPhase(LlvmAggregatePhaseName); + Log::phase(LlvmAggregatePhaseName); LastPhase = LlvmAggregatePhaseName; } // print all - // llvm_support::printPhase(PhaseName); + // Log::phase(PhaseName); // LastPhase = PhaseArg; } return false; @@ -430,9 +430,41 @@ static inline void addPass( } + +/** + * TODO: this function has exact copy located in retdec-decompiler.cpp. + * The reason for this is that right now creation of correct interface that + * would hold this function is much more time expensive than hard copy. + * + * Before merging these two methods (providing them suitable interface) + * each change in one of them must be reflected to both. + */ +void setLogsFrom(const retdec::config::Parameters& params) +{ + auto logFile = params.getLogFile(); + auto errFile = params.getErrFile(); + auto verbose = params.isVerboseOutput(); + + Logger::Ptr outLog = nullptr; + + outLog.reset( + logFile.empty() + ? new Logger(std::cout, verbose) + : new FileLogger(logFile, verbose) + ); + + Log::set(Log::Type::Info, std::move(outLog)); + + if (!errFile.empty()) { + Log::set(Log::Type::Error, Logger::Ptr(new FileLogger(errFile))); + } +} + bool decompile(retdec::config::Config& config, std::string* outString) { - llvm_support::printPhase("Initialization"); + setLogsFrom(config.parameters); + + Log::phase("Initialization"); auto& passRegistry = initializeLlvmPasses(); // limitMaximalMemoryIfRequested(params); diff --git a/src/retdectool/retdec.cpp b/src/retdectool/retdec.cpp index 45ffe35bf..357efd5af 100644 --- a/src/retdectool/retdec.cpp +++ b/src/retdectool/retdec.cpp @@ -5,9 +5,11 @@ */ #include -#include #include "retdec/retdec/retdec.h" +#include "retdec/utils/io/log.h" + +using namespace retdec::utils::io; class ProgramOptions { @@ -53,14 +55,14 @@ class ProgramOptions void dump() { - std::cout << std::endl; - std::cout << "Program Options:" << std::endl; - std::cout << "\t" << "input file : " << inputFile << std::endl; + Log::info() << std::endl; + Log::info() << "Program Options:" << std::endl; + Log::info() << "\t" << "input file : " << inputFile << std::endl; } void printHelpAndDie() { - std::cout << _programName << ":\n" + Log::info() << _programName << ":\n" << "\t-i inputFile\n"; exit(EXIT_SUCCESS); @@ -83,52 +85,52 @@ int main(int argc, char **argv) for (auto& f : fs) { - std::cout << std::endl; - std::cout << f.getName() << " @ " << f << std::endl; + Log::info() << std::endl; + Log::info() << f.getName() << " @ " << f << std::endl; - std::cout << std::endl; - std::cout << "\t" << "code refs (insns referencing this function):" + Log::info() << std::endl; + Log::info() << "\t" << "code refs (insns referencing this function):" << std::endl; for (auto& r : f.codeReferences) { auto* f = fs.getRange(r); - std::cout << "\t\t" << r + Log::info() << "\t\t" << r << " ( @ " << (f ? f->getName() : "unknown") << " )" << std::endl; } for (auto& bb : f.basicBlocks) { - std::cout << std::endl; - std::cout << "\t" << "bb @ " << bb << std::endl; + Log::info() << std::endl; + Log::info() << "\t" << "bb @ " << bb << std::endl; - std::cout << "\t\t" << "preds:" << std::endl; + Log::info() << "\t\t" << "preds:" << std::endl; for (auto p : bb.preds) { - std::cout << "\t\t\t" << p << std::endl; + Log::info() << "\t\t\t" << p << std::endl; } - std::cout << "\t\t" << "succs:" << std::endl; + Log::info() << "\t\t" << "succs:" << std::endl; for (auto s : bb.succs) { - std::cout << "\t\t\t" << s << std::endl; + Log::info() << "\t\t\t" << s << std::endl; } - std::cout << "\t\t" << "calls:" << std::endl; + Log::info() << "\t\t" << "calls:" << std::endl; for (auto c : bb.calls) { auto* f = fs.getRange(c.targetAddr); - std::cout << "\t\t\t" << c.srcAddr << " -> " << c.targetAddr + Log::info() << "\t\t\t" << c.srcAddr << " -> " << c.targetAddr << " ( @ " << (f ? f->getName() : "unknown") << " )" << std::endl; } // These are not only text entries!!! // There is a full Capstone representation for every instruction. - std::cout << "\t\t" << "instructions:" << std::endl; + Log::info() << "\t\t" << "instructions:" << std::endl; for (auto* insn : bb.instructions) { - std::cout << "\t\t\t" << retdec::common::Address(insn->address) + Log::info() << "\t\t\t" << retdec::common::Address(insn->address) << " @ " << insn->mnemonic << " " << insn->op_str << std::endl; } diff --git a/src/stacofin/stacofin.cpp b/src/stacofin/stacofin.cpp index f8a189d61..39b3eedcd 100644 --- a/src/stacofin/stacofin.cpp +++ b/src/stacofin/stacofin.cpp @@ -4,7 +4,6 @@ * @copyright (c) 2017 Avast Software, licensed under the MIT license */ -#include #include #include diff --git a/src/stacofintool/stacofin.cpp b/src/stacofintool/stacofin.cpp index a127ac8c7..d962adfae 100644 --- a/src/stacofintool/stacofin.cpp +++ b/src/stacofintool/stacofin.cpp @@ -5,15 +5,16 @@ */ #include -#include #include #include #include "retdec/utils/filesystem.h" +#include "retdec/utils/io/log.h" #include "retdec/stacofin/stacofin.h" #include "retdec/loader/image_factory.h" using namespace retdec::utils; +using namespace retdec::utils::io; using namespace retdec::stacofin; using namespace retdec::loader; @@ -22,7 +23,7 @@ using namespace retdec::loader; */ void printUsage() { - std::cout << "\nStatic code detection tool.\n" + Log::info() << "\nStatic code detection tool.\n" << "Usage: stacofin -b BINARY_FILE YARA_FILE [YARA_FILE ...]\n\n"; } @@ -35,7 +36,7 @@ void printUsage() int printError( const std::string &errorMessage) { - std::cerr << "Error: " << errorMessage << "\n"; + Log::error() << Log::Error << errorMessage << "\n"; return 1; } @@ -69,16 +70,16 @@ void printDetectionsDebug( auto& detected = p.second; if (detected.getAddress() == lastAddress) { for (const auto &name : detected.names) { - std::cout << "or " << name << "\n"; + Log::info() << "or " << name << "\n"; } continue; } lastAddress = detected.getAddress(); - std::cout << "0x" << std::setfill('0') << std::setw(8) << std::hex + Log::info() << "0x" << std::setfill('0') << std::setw(8) << std::hex << detected.getAddress() << " " << detected.names[0] << "\n"; for (std::size_t i = 1; i < detected.names.size(); ++i) { - std::cout << "or " << detected.names[i] << "\n"; + Log::info() << "or " << detected.names[i] << "\n"; } } } @@ -96,18 +97,18 @@ void printDetections( auto& detected = p.second; if (detected.getAddress() == lastAddress) { for (const auto &name : detected.names) { - std::cout << "\t\t\t" << name << " " + Log::info() << "\t\t\t" << name << " " << referencesToString(detected.references) << "\n";; } continue; } lastAddress = detected.getAddress(); - std::cout << "0x" << std::hex << detected.getAddress() << " \t" + Log::info() << "0x" << std::hex << detected.getAddress() << " \t" << std::dec << detected.size << "\t" << detected.names[0] << " " << referencesToString(detected.references) << "\n"; for (std::size_t i = 1; i < detected.names.size(); ++i) { - std::cout << "\t\t\t" << detected.names[i] << " " + Log::info() << "\t\t\t" << detected.names[i] << " " << referencesToString(detected.references) << "\n";; } } @@ -173,7 +174,7 @@ int doActions( for (auto it = coverage.begin(), e = coverage.end(); it != e; ++it) { totalCoverage += it->getSize(); } - std::cout << "\nTotal code coverage is " << totalCoverage << " bytes.\n"; + Log::info() << "\nTotal code coverage is " << totalCoverage << " bytes.\n"; return 0; } diff --git a/src/unpackertool/plugin_mgr.cpp b/src/unpackertool/plugin_mgr.cpp index 347c79759..385fda469 100644 --- a/src/unpackertool/plugin_mgr.cpp +++ b/src/unpackertool/plugin_mgr.cpp @@ -4,7 +4,6 @@ * @copyright (c) 2017 Avast Software, licensed under the MIT license */ -#include #include #include "retdec/utils/string.h" diff --git a/src/unpackertool/unpacker.cpp b/src/unpackertool/unpacker.cpp index a75d8edc6..8ffafa490 100644 --- a/src/unpackertool/unpacker.cpp +++ b/src/unpackertool/unpacker.cpp @@ -5,11 +5,11 @@ */ #include -#include #include #include "retdec/utils/conversion.h" #include "retdec/utils/filesystem.h" +#include "retdec/utils/io/log.h" #include "retdec/utils/memory.h" #include "retdec/cpdetect/cpdetect.h" #include "retdec/fileformat/fileformat.h" @@ -19,6 +19,7 @@ #include "plugin_mgr.h" using namespace retdec::utils; +using namespace retdec::utils::io; using namespace retdec::unpacker; using namespace retdec::unpackertool; @@ -48,17 +49,17 @@ bool detectPackers(const std::string& inputFile, std::vectorused) { - std::cout << handler << std::endl; + Log::info() << handler << std::endl; } // -p|--plugins else if (handler["plugins"]->used) { - std::cout << "List of available plugins:" << std::endl; + Log::info() << "List of available plugins:" << std::endl; for (const auto& plugin : PluginMgr::plugins) { const Plugin::Info* info = plugin->getInfo(); - std::cout << info->name << " " << info->pluginVersion + Log::info() << info->name << " " << info->pluginVersion << " for packer '" << info->name << " " << info->packerVersion << "' (" << info->author << ")" << std::endl; } @@ -192,7 +193,7 @@ ExitCode processArgs(ArgHandler& handler, char argc, char** argv) } // Nothing else, just print the help else - std::cout << handler << std::endl; + Log::info() << handler << std::endl; return EXIT_CODE_OK; } diff --git a/src/utils/CMakeLists.txt b/src/utils/CMakeLists.txt index 36eed5f69..90ec2a70d 100644 --- a/src/utils/CMakeLists.txt +++ b/src/utils/CMakeLists.txt @@ -13,6 +13,8 @@ add_library(utils STATIC system.cpp time.cpp ${RETDEC_DEPS_DIR}/whereami/whereami/whereami.c + io/log.cpp + io/logger.cpp ) add_library(retdec::utils ALIAS utils) diff --git a/src/utils/io/log.cpp b/src/utils/io/log.cpp new file mode 100644 index 000000000..16b5d455c --- /dev/null +++ b/src/utils/io/log.cpp @@ -0,0 +1,76 @@ +/** +* @file src/utils/io/logger.cpp +* @brief Provides unified logging interface. +* @copyright (c) 2020 Avast Software, licensed under the MIT license +*/ + +#include + +#include "retdec/utils/io/log.h" + +namespace retdec { +namespace utils { +namespace io { + +// Initialization of shortcuts +const Log::Action Log::Error = Log::Action::Error; +const Log::Action Log::Warning = Log::Action::Warning; +const Log::Action Log::Phase = Log::Action::Phase; +const Log::Action Log::SubPhase = Log::Action::SubPhase; +const Log::Action Log::SubSubPhase = Log::Action::SubSubPhase; +const Log::Action Log::ElapsedTime = Log::Action::ElapsedTime; + +Logger::Ptr Log::writers[] = { + /*Info*/ /*default*/ nullptr, + /*Debug*/ /*default*/ nullptr, + /*Error*/ Logger::Ptr(new Logger(std::cerr)), + /*Undefined*/ Logger::Ptr(new Logger(std::cout, false)) +}; + +Logger Log::defaultLogger(std::cout, true); + +Logger& Log::get(const Log::Type& logType) +{ + // This can happen only after adding new Log::Type + // after Log::Type::Undefined in Log::Type enum. + assert(static_cast(logType) <= static_cast(Log::Type::Undefined)); + + if (auto logger = writers[static_cast(logType)].get()) + return *logger; + + // Fallback usage of logger. + return defaultLogger; +} + +void Log::set(const Log::Type& lt, Logger::Ptr&& logger) +{ + // This can happen only after adding new Log::Type + // after Log::Type::Undefined in Log::Type enum. + assert(static_cast(lt) <= static_cast(Log::Type::Undefined)); + + writers[static_cast(lt)] = std::move(logger); +} + +Logger Log::info() +{ + return get(Log::Type::Info); +} + +void Log::phase(const std::string& phase, const Log::Action& action) +{ + Log::info() << action << phase << Log::ElapsedTime << std::endl; +} + +Logger Log::debug() +{ + return Logger(get(Log::Type::Debug)); +} + +Logger Log::error() +{ + return Logger(get(Log::Type::Error)); +} + +} +} +} diff --git a/src/utils/io/logger.cpp b/src/utils/io/logger.cpp new file mode 100644 index 000000000..1131e76ce --- /dev/null +++ b/src/utils/io/logger.cpp @@ -0,0 +1,157 @@ +/** +* @file src/utils/io/logger.cpp +* @brief Implementation of a logging class. +* @copyright (c) 2020 Avast Software, licensed under the MIT license +*/ + +#include +#include +#include + +#include "retdec/utils/io/logger.h" +#include "retdec/utils/os.h" +#include "retdec/utils/time.h" + +#ifdef OS_WINDOWS +#include +#include +#else +#include +#endif + +namespace retdec { +namespace utils { +namespace io { + +////////// +// +// Logger +// +////// + +Logger::Logger(std::ostream& stream, bool verbose): + _out(stream), + _verbose(verbose) +{ +#ifdef OS_WINDOWS + // On windows we need to try to set ENABLE_VIRTUAL_TERMINAL_PROCESSING. + // This will enable ANSI support in terminal. This is best effort + // implementation approach. + // + // Source: https://docs.microsoft.com/en-us/windows/console/console-virtual-terminal-sequences + _terminalNotSupported = true; + HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE); + if (hOut == INVALID_HANDLE_VALUE) + return; + + DWORD dwMode = 0; + if (!GetConsoleMode(hOut, &dwMode)) + return; + + _modifiedTerminalProperty = dwMode & ENABLE_VIRTUAL_TERMINAL_PROCESSING; + dwMode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING; + if (!SetConsoleMode(hOut, dwMode)) + return; + + _terminalNotSupported = false; +#endif +} + +Logger::Logger(const Logger& from): + Logger(from._out, from._verbose) +{ + _currentBrush = from._currentBrush; +} + +Logger::~Logger() +{ + if (_currentBrush != Color::Default) + *this << Color::Default; +} + +Logger& Logger::operator << (const Action& p) +{ + if (p == Phase) { + return *this << Color::Yellow << "Running phase: "; + } + if (p == SubPhase) { + return *this << Color::Yellow << " -> "; + } + if (p == SubSubPhase) { + return *this << Color::Yellow << " -> "; + } + if (p == SubSubPhase) { + return *this << Color::Yellow << " -> "; + } + if (p == Error) { + return *this << "Error: "; + } + if (p == Warning) { + return *this << Color::DarkCyan << "Warning: "; + } + if (p == ElapsedTime) { + std::stringstream formatted; + formatted << std::fixed << std::setprecision(2) << getElapsedTime(); + return *this << " ( " << formatted.str() << "s )"; + } + + return *this; +} + +Logger& Logger::operator << (const Color& lc) +{ + static std::string ansiMap[static_cast(Color::Default)+1] = { + /*Color::Red*/ "\u001b[0;1;31m", + /*Color::Green*/ "\u001b[0;1;32m", + /*Color::Blue*/ "\u001b[0;1;34m", + /*Color::Yellow*/ "\u001b[0;1;33m", + /*Color::DarkCyan*/"\u001b[0;1;36m", + /*Color::Default*/ "\u001b[0m" + }; + + if (_terminalNotSupported || isRedirected(_out)) + return *this; + + _currentBrush = lc; + return *this << ansiMap[static_cast(lc)]; +} + +bool Logger::isRedirected(const std::ostream& stream) const +{ +#ifdef OS_WINDOWS + // On windows POSIX functions isatty and fileno + // generate Warning. + auto isConsole = _isatty; + auto fileDescriptor = _fileno; +#else + auto isConsole = isatty; + auto fileDescriptor = fileno; +#endif + + if (stream.rdbuf() == std::cout.rdbuf()) { + return isConsole(fileDescriptor(stdout)) == 0; + } + else if (stream.rdbuf() == std::cerr.rdbuf()) { + return isConsole(fileDescriptor(stderr)) == 0; + } + + return true; +} + +////////// +// +// FileLogger +// +////// + +FileLogger::FileLogger(const std::string& file, bool verbose): + Logger(_file, verbose) +{ + _file.open(file, std::ofstream::out); + if (!_file) + throw std::runtime_error("unable to open file \""+file+"\" for writing."); +} + +} +} +} diff --git a/support/decompiler-config.json b/support/decompiler-config.json index 6f08f59c8..c5ec87077 100644 --- a/support/decompiler-config.json +++ b/support/decompiler-config.json @@ -1,6 +1,6 @@ { "decompParams": { - "verboseOut": false, + "verboseOut": true, "outputFormat": "plain", "keepAllFuncs": false, "selectedDecodeOnly": false, diff --git a/tests/llvmir2hll/pattern/pattern_finder_runners/cli_pattern_finder_runner_tests.cpp b/tests/llvmir2hll/pattern/pattern_finder_runners/cli_pattern_finder_runner_tests.cpp index 58a5f6184..73c100d5e 100644 --- a/tests/llvmir2hll/pattern/pattern_finder_runners/cli_pattern_finder_runner_tests.cpp +++ b/tests/llvmir2hll/pattern/pattern_finder_runners/cli_pattern_finder_runner_tests.cpp @@ -14,8 +14,10 @@ #include "retdec/llvmir2hll/pattern/pattern_finder_runners/cli_pattern_finder_runner.h" #include "llvmir2hll/pattern/pattern_finder_mock.h" #include "llvmir2hll/pattern/pattern_mock.h" +#include "retdec/utils/io/log.h" using namespace ::testing; +using namespace retdec::utils::io; namespace retdec { namespace llvmir2hll { @@ -37,8 +39,8 @@ RunWithOnePatternFinderCallsFindPatternAndPrintOnThatFinder) { NiceMock *pfMock(new NiceMock(va, cio)); ShPtr pf(pfMock); - std::string outputStr; - llvm::raw_string_ostream os(outputStr); + std::stringstream outputStr; + Logger os(outputStr); // Expectations. PatternFinder::Patterns patterns; @@ -54,9 +56,9 @@ RunWithOnePatternFinderCallsFindPatternAndPrintOnThatFinder) { // Test. pfr->run(pf, module); - ASSERT_FALSE(os.str().empty()); + ASSERT_FALSE(outputStr.str().empty()); // The ID of the pattern finder should be present in the output. - EXPECT_TRUE(os.str().find(PF_MOCK_ID) != std::string::npos); + EXPECT_TRUE(outputStr.str().find(PF_MOCK_ID) != std::string::npos); } TEST_F(CLIPatternFinderRunnerTests, @@ -64,8 +66,8 @@ RunWithTwoPatternFindersCallsFindPatternAndPrintOnTheseFinders) { INSTANTIATE_ALIAS_ANALYSIS_AND_VALUE_ANALYSIS(module); INSTANTIATE_CALL_INFO_OBTAINER_MOCK(); - std::string outputStr; - llvm::raw_string_ostream os(outputStr); + std::stringstream outputStr; + Logger os(outputStr); // Mocks. NiceMock *p1Mock(new NiceMock()); @@ -102,10 +104,10 @@ RunWithTwoPatternFindersCallsFindPatternAndPrintOnTheseFinders) { // Test. pfr->run(pfs, module); - ASSERT_FALSE(os.str().empty()); + ASSERT_FALSE(outputStr.str().empty()); // The IDs of the pattern finders should be present in the output. - EXPECT_TRUE(os.str().find(PF1_MOCK_ID) != std::string::npos); - EXPECT_TRUE(os.str().find(PF2_MOCK_ID) != std::string::npos); + EXPECT_TRUE(outputStr.str().find(PF1_MOCK_ID) != std::string::npos); + EXPECT_TRUE(outputStr.str().find(PF2_MOCK_ID) != std::string::npos); } } // namespace tests