From cfc71fd6fb1e7460b81d7a6c519a551b99fb0d29 Mon Sep 17 00:00:00 2001 From: Hugo Date: Fri, 30 Jan 2026 04:40:11 +0100 Subject: [PATCH 1/5] chore: add clang-format configuration --- .clang-format | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 .clang-format diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..0149e40 --- /dev/null +++ b/.clang-format @@ -0,0 +1,12 @@ +BasedOnStyle: LLVM +Standard: c++17 +IndentWidth: 4 +TabWidth: 4 +UseTab: Never +BreakBeforeBraces: Allman +AllowShortFunctionsOnASingleLine: Empty +ColumnLimit: 100 +PointerAlignment: Left +NamespaceIndentation: All +SortIncludes: Never +ReflowComments: false From 21ecea57ba1aa7007489ce2a031e26c35fcfedac Mon Sep 17 00:00:00 2001 From: Hugo Date: Fri, 30 Jan 2026 04:45:11 +0100 Subject: [PATCH 2/5] chore: add clang-format scripts and CI --- .github/workflows/clang-format.yml | 22 ++++++++++++++++++++ CMakeLists.txt | 13 ++++++++++++ README.md | 8 ++++++++ scripts/format-check.sh | 32 ++++++++++++++++++++++++++++++ scripts/format.sh | 20 +++++++++++++++++++ 5 files changed, 95 insertions(+) create mode 100644 .github/workflows/clang-format.yml create mode 100755 scripts/format-check.sh create mode 100755 scripts/format.sh diff --git a/.github/workflows/clang-format.yml b/.github/workflows/clang-format.yml new file mode 100644 index 0000000..ea07d8b --- /dev/null +++ b/.github/workflows/clang-format.yml @@ -0,0 +1,22 @@ +name: clang-format + +on: + push: + branches: [main, master] + pull_request: + +jobs: + format-check: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Install clang-format 17 + run: | + sudo apt-get update + sudo apt-get install -y clang-format-17 + sudo update-alternatives --install /usr/bin/clang-format clang-format /usr/bin/clang-format-17 100 + + - name: Run format-check + run: ./scripts/format-check.sh diff --git a/CMakeLists.txt b/CMakeLists.txt index 8a10c92..4809665 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -80,3 +80,16 @@ endif() include(${CMAKE_SOURCE_DIR}/cmake/tscancode.cmake) include(${CMAKE_SOURCE_DIR}/cmake/flawfinder.cmake) # include(${CMAKE_SOURCE_DIR}/cmake/ikos.cmake) + +# ============ +# FORMATTING +# ============ +add_custom_target(format + COMMAND "${CMAKE_CURRENT_SOURCE_DIR}/scripts/format.sh" + WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" + COMMENT "Run clang-format on source files") + +add_custom_target(format-check + COMMAND "${CMAKE_CURRENT_SOURCE_DIR}/scripts/format-check.sh" + WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" + COMMENT "Verify clang-format compliance") diff --git a/README.md b/README.md index 9ff86da..40ee9f8 100644 --- a/README.md +++ b/README.md @@ -19,6 +19,14 @@ cmake .. \ make -j4 ``` +### CODE STYLE (clang-format) + +- Version cible : `clang-format` 17 (utilisée dans la CI). +- Formater localement : `./scripts/format.sh` +- Vérifier sans modifier : `./scripts/format-check.sh` +- CMake : `cmake --build build --target format` ou `--target format-check` +- CI : le job GitHub Actions `clang-format` échoue si un fichier n’est pas formaté. + ### DEBUG You can pass arguments to CMake and invoke ASan. diff --git a/scripts/format-check.sh b/scripts/format-check.sh new file mode 100755 index 0000000..31cf60e --- /dev/null +++ b/scripts/format-check.sh @@ -0,0 +1,32 @@ +#!/usr/bin/env bash +set -euo pipefail + +SCRIPT_DIR="$(cd -- "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +REPO_ROOT="$(cd -- "${SCRIPT_DIR}/.." && pwd)" + +files=() +while IFS= read -r -d '' file; do + files+=("$file") +done < <(find "${REPO_ROOT}" \ + \( -path "${REPO_ROOT}/build" -o -path "${REPO_ROOT}/external" -o -path "${REPO_ROOT}/cmake" -o -path "${REPO_ROOT}/.git" \) -prune -o \ + -type f \( -name '*.c' -o -name '*.cc' -o -name '*.cpp' -o -name '*.cxx' -o -name '*.h' -o -name '*.hh' -o -name '*.hpp' -o -name '*.hxx' \) -print0) + +if [ "${#files[@]}" -eq 0 ]; then + echo "No source files to check." + exit 0 +fi + +echo "Checking formatting on ${#files[@]} files..." +failed=0 +for file in "${files[@]}"; do + if ! clang-format --dry-run --Werror "${file}"; then + failed=1 + fi +done + +if [ "${failed}" -ne 0 ]; then + echo "Formatting check failed. Run scripts/format.sh to fix." + exit 1 +fi + +echo "Formatting is clean." diff --git a/scripts/format.sh b/scripts/format.sh new file mode 100755 index 0000000..4fc02be --- /dev/null +++ b/scripts/format.sh @@ -0,0 +1,20 @@ +#!/usr/bin/env bash +set -euo pipefail + +SCRIPT_DIR="$(cd -- "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +REPO_ROOT="$(cd -- "${SCRIPT_DIR}/.." && pwd)" + +files=() +while IFS= read -r -d '' file; do + files+=("$file") +done < <(find "${REPO_ROOT}" \ + \( -path "${REPO_ROOT}/build" -o -path "${REPO_ROOT}/external" -o -path "${REPO_ROOT}/cmake" -o -path "${REPO_ROOT}/.git" \) -prune -o \ + -type f \( -name '*.c' -o -name '*.cc' -o -name '*.cpp' -o -name '*.cxx' -o -name '*.h' -o -name '*.hh' -o -name '*.hpp' -o -name '*.hxx' \) -print0) + +if [ "${#files[@]}" -eq 0 ]; then + echo "No source files to format." + exit 0 +fi + +echo "Formatting ${#files[@]} files with clang-format (style from ${REPO_ROOT}/.clang-format)..." +clang-format -i "${files[@]}" From e8e8ccfc6a6356dcee1d000702e2c88e9f375f69 Mon Sep 17 00:00:00 2001 From: Hugo Date: Fri, 30 Jan 2026 14:48:14 +0100 Subject: [PATCH 3/5] style: format codebase with clang-format --- include/App/Config.hpp | 2 +- include/App/Runner.hpp | 2 +- include/ArgumentParser/ArgumentManager.hpp | 36 +- include/ArgumentParser/BaseArgumentParser.hpp | 24 +- .../CLI11/CLI11ArgumentParser.hpp | 56 +- .../GetOpt/GetOptArgumentParser.hpp | 23 +- include/ArgumentParser/IArgumentParser.hpp | 42 +- include/ArgumentParser/OptionInfo.hpp | 10 +- include/Config/config.hpp | 269 ++++---- include/Process/Ipc/HttpServer.hpp | 599 +++++++++--------- include/Process/Ipc/IpcStrategy.hpp | 42 +- include/Process/LinuxProcess.hpp | 18 +- include/Process/Process.hpp | 46 +- include/Process/ProcessFactory.hpp | 36 +- include/Process/ThreadProcess.hpp | 71 +-- include/Process/Tools/AnalysisTools.hpp | 211 +++--- include/Process/Tools/AnalysisToolsBase.hpp | 10 +- include/Process/Tools/IAnalysisTools.hpp | 26 +- include/Process/Tools/ToolsInvoker.hpp | 109 ++-- include/Process/UnixProcess.hpp | 118 ++-- include/Process/WinProcess.hpp | 47 +- include/attributes.hpp | 20 +- include/ctrace_defs/types.hpp | 13 +- include/ctrace_tools/colors.hpp | 18 +- include/ctrace_tools/languageType.hpp | 2 +- include/ctrace_tools/mangle.hpp | 21 +- include/ctrace_tools/strings.hpp | 12 +- main.cpp | 5 +- src/App/Config.cpp | 13 +- src/App/Files.cpp | 50 +- src/App/Runner.cpp | 34 +- src/ArgumentParser/ArgumentManager.cpp | 19 +- src/ArgumentParser/ArgumentParserFactory.cpp | 3 +- src/ArgumentParser/BaseArgumentParser.cpp | 9 +- .../CLI11/CLI11ArgumentParser.cpp | 111 ++-- .../GetOpt/GetoptArgumentParser.cpp | 73 ++- .../Tools/StackAnalyzerToolImplementation.cpp | 82 +-- .../Tools/TscancodeToolImplementation.cpp | 221 +++---- src/ctrace_tools/languageType.cpp | 20 +- src/ctrace_tools/mangle.cpp | 18 +- src/ctrace_tools/strings.cpp | 79 ++- .../AvoidDefaultArgumentsOnVirtualMethods.hh | 14 +- tests/DestructorOfVirtualClass.hh | 23 +- tests/EmptyForStatement.cc | 6 +- tests/RedundantIfStatement.c | 10 +- tests/RedundantIfStatement.cc | 10 +- tests/bad_function_pointer.cc | 11 +- tests/bound_index.cc | 4 +- tests/buffer_overflow.cc | 13 +- tests/dead_code.cc | 12 +- tests/double_free.c | 9 +- tests/format_problem.c | 2 +- tests/if_collapse.c | 6 +- tests/if_constant_expr.c | 3 +- tests/null_pointer.c | 13 +- tests/partitioning.cc | 41 +- tests/pointer_comparison_analysis.cc | 23 +- tests/pointer_overflow.cc | 8 +- tests/too_many_methods.cc | 29 +- tests/unaligned_dereferencing.cc | 8 +- 60 files changed, 1561 insertions(+), 1304 deletions(-) diff --git a/include/App/Config.hpp b/include/App/Config.hpp index 00aa2ac..54f890c 100644 --- a/include/App/Config.hpp +++ b/include/App/Config.hpp @@ -6,7 +6,7 @@ namespace ctrace { - CT_NODISCARD ProgramConfig buildConfig(int argc, char *argv[]); + CT_NODISCARD ProgramConfig buildConfig(int argc, char* argv[]); } #endif // APP_CONFIG_HPP diff --git a/include/App/Runner.hpp b/include/App/Runner.hpp index c39bd8b..ee8d820 100644 --- a/include/App/Runner.hpp +++ b/include/App/Runner.hpp @@ -8,6 +8,6 @@ namespace ctrace { CT_NODISCARD int run_server(const ProgramConfig& config); CT_NODISCARD int run_cli_analysis(const ProgramConfig& config); -} +} // namespace ctrace #endif // APP_RUNNER_HPP diff --git a/include/ArgumentParser/ArgumentManager.hpp b/include/ArgumentParser/ArgumentManager.hpp index 38cdcfa..cf410d6 100644 --- a/include/ArgumentParser/ArgumentManager.hpp +++ b/include/ArgumentParser/ArgumentManager.hpp @@ -13,24 +13,24 @@ */ class ArgumentManager { - public: - /** + public: + /** * @brief Constructs an `ArgumentManager` with a given argument parser. * * @param parser A `std::unique_ptr` to an `IArgumentParser` implementation. */ - explicit ArgumentManager(std::unique_ptr parser); + explicit ArgumentManager(std::unique_ptr parser); - /** + /** * @brief Adds an option to the argument parser. * * @param name The name of the option (e.g., "--option"). * @param hasArgument Indicates whether the option requires an argument. * @param shortName A single-character shorthand for the option (e.g., '-o'). */ - void addOption(const std::string& name, bool hasArgument, char shortName); + void addOption(const std::string& name, bool hasArgument, char shortName); - /** + /** * @brief Adds a flag to the argument parser. * * Flags are options that do not require arguments (e.g., "--verbose"). @@ -38,42 +38,42 @@ class ArgumentManager * @param name The name of the flag (e.g., "--flag"). * @param shortName A single-character shorthand for the flag (e.g., '-f'). */ - void addFlag(const std::string& name, char shortName); + void addFlag(const std::string& name, char shortName); - /** + /** * @brief Parses the command-line arguments. * * @param argc The number of arguments. * @param argv The array of argument strings. */ - void parse(int argc, char* argv[]); + void parse(int argc, char* argv[]); - /** + /** * @brief Checks if a specific option was provided. * * @param name The name of the option to check. * @return `true` if the option was provided, `false` otherwise. */ - [[nodiscard]] bool hasOption(const std::string& name) const; + [[nodiscard]] bool hasOption(const std::string& name) const; - /** + /** * @brief Checks if no options were provided. * * @return `true` if no options were provided, `false` otherwise. */ - [[nodiscard]] bool hasNoOption() const; + [[nodiscard]] bool hasNoOption() const; - /** + /** * @brief Retrieves the value of a specific option. * * @param name The name of the option. * @return The value of the option as a `std::string`. */ - [[nodiscard]] std::string getOptionValue(const std::string& name) const; + [[nodiscard]] std::string getOptionValue(const std::string& name) const; - private: - std::unique_ptr _parser; ///< The underlying argument parser implementation. - bool _hasNoOption = false; ///< Flag indicating if no options were provided. + private: + std::unique_ptr _parser; ///< The underlying argument parser implementation. + bool _hasNoOption = false; ///< Flag indicating if no options were provided. }; #endif // ARGUMENTMANAGER_HPP diff --git a/include/ArgumentParser/BaseArgumentParser.hpp b/include/ArgumentParser/BaseArgumentParser.hpp index bd53af0..cd39e38 100644 --- a/include/ArgumentParser/BaseArgumentParser.hpp +++ b/include/ArgumentParser/BaseArgumentParser.hpp @@ -13,11 +13,11 @@ */ class BaseArgumentParser : public IArgumentParser { - protected: - ErrorCode _lastError; ///< Stores the last error code encountered. - std::string _errorMessage; ///< Stores the last error message encountered. + protected: + ErrorCode _lastError; ///< Stores the last error code encountered. + std::string _errorMessage; ///< Stores the last error message encountered. - /** + /** * @brief Sets the last error code and message. * * This method updates the internal error state with the provided @@ -26,30 +26,30 @@ class BaseArgumentParser : public IArgumentParser * @param code The error code to set. * @param message The error message to set (default is an empty string). */ - void setError(ErrorCode code, const std::string& message = ""); + void setError(ErrorCode code, const std::string& message = ""); - public: - /** + public: + /** * @brief Default constructor for `BaseArgumentParser`. * * Initializes the error state to `ErrorCode::SUCCESS` and clears * the error message. */ - BaseArgumentParser(); + BaseArgumentParser(); - /** + /** * @brief Retrieves the last error code. * * @return An `ErrorCode` representing the last error encountered. */ - ErrorCode getLastError() const override; + ErrorCode getLastError() const override; - /** + /** * @brief Retrieves the last error message. * * @return A `std::string` containing the last error message. */ - std::string getErrorMessage() const override; + std::string getErrorMessage() const override; }; #endif // BASE_ARGUMENT_PARSER_HPP diff --git a/include/ArgumentParser/CLI11/CLI11ArgumentParser.hpp b/include/ArgumentParser/CLI11/CLI11ArgumentParser.hpp index 021b0d8..9fe0086 100644 --- a/include/ArgumentParser/CLI11/CLI11ArgumentParser.hpp +++ b/include/ArgumentParser/CLI11/CLI11ArgumentParser.hpp @@ -15,13 +15,13 @@ */ class CLI11ArgumentParser : public BaseArgumentParser { - private: - CLI::App app{"CLI11 Argument Parser"}; ///< CLI11 application instance. - std::vector _options; ///< List of registered options. - std::vector> _flags; ///< List of flag options (no arguments). - std::vector> _values; ///< List of options with arguments. + private: + CLI::App app{"CLI11 Argument Parser"}; ///< CLI11 application instance. + std::vector _options; ///< List of registered options. + std::vector> _flags; ///< List of flag options (no arguments). + std::vector> _values; ///< List of options with arguments. - /** + /** * @brief Checks if an option already exists. * * This method verifies whether an option with the given name or short name @@ -31,19 +31,19 @@ class CLI11ArgumentParser : public BaseArgumentParser * @param shortName The short name of the option. * @return `true` if the option exists, `false` otherwise. */ - [[nodiscard]] bool optionExists(const std::string& name, char shortName) const + [[nodiscard]] bool optionExists(const std::string& name, char shortName) const + { + for (const auto& opt : _options) { - for (const auto& opt : _options) + if (opt.name == name || (shortName != '\0' && opt.shortName == shortName)) { - if (opt.name == name || (shortName != '\0' && opt.shortName == shortName)) - { - return true; - } + return true; } - return false; } + return false; + } - /** + /** * @brief Removes leading dashes from an option name. * * This helper function strips leading dashes (`-` or `--`) from an option name @@ -52,17 +52,17 @@ class CLI11ArgumentParser : public BaseArgumentParser * @param name The option name to process. * @return A string without leading dashes. */ - static std::string stripDashes(const std::string& name) + static std::string stripDashes(const std::string& name) + { + std::string stripped = name; + while (!stripped.empty() && stripped[0] == '-') { - std::string stripped = name; - while (!stripped.empty() && stripped[0] == '-') - { - stripped.erase(0, 1); - } - return stripped; + stripped.erase(0, 1); } + return stripped; + } -public: + public: /** * @brief Constructs a `CLI11ArgumentParser` instance. * @@ -104,7 +104,8 @@ class CLI11ArgumentParser : public BaseArgumentParser { auto* value = new std::string(); _values.emplace_back(longOpt, value); - auto* cliOption = app.add_option("-" + shortOpt + "," + name, *value, "Option with argument"); + auto* cliOption = + app.add_option("-" + shortOpt + "," + name, *value, "Option with argument"); if (!cliOption) { setError(ErrorCode::INVALID_OPTION, "Failed to add option: " + name); @@ -166,7 +167,8 @@ class CLI11ArgumentParser : public BaseArgumentParser for (auto& opt : _options) { std::string longOpt = stripDashes(opt.name); - if (opt.hasArgument) { + if (opt.hasArgument) + { for (const auto& [name, value] : _values) { if (name == longOpt && !value->empty()) @@ -203,7 +205,8 @@ class CLI11ArgumentParser : public BaseArgumentParser { for (const auto& opt : _options) { - if (opt.name == name || (opt.shortName != '\0' && std::string(1, opt.shortName) == name)) + if (opt.name == name || + (opt.shortName != '\0' && std::string(1, opt.shortName) == name)) { return opt.isSet; } @@ -222,7 +225,8 @@ class CLI11ArgumentParser : public BaseArgumentParser { for (const auto& opt : _options) { - if (opt.name == name || (opt.shortName != '\0' && std::string(1, opt.shortName) == name)) + if (opt.name == name || + (opt.shortName != '\0' && std::string(1, opt.shortName) == name)) { return opt.value; } diff --git a/include/ArgumentParser/GetOpt/GetOptArgumentParser.hpp b/include/ArgumentParser/GetOpt/GetOptArgumentParser.hpp index 99be748..c1686d2 100644 --- a/include/ArgumentParser/GetOpt/GetOptArgumentParser.hpp +++ b/include/ArgumentParser/GetOpt/GetOptArgumentParser.hpp @@ -19,10 +19,10 @@ */ class GetoptArgumentParser : public BaseArgumentParser { -private: + private: std::vector _longOptions; ///< List of long options for `getopt_long`. - std::vector _options; ///< List of registered options. - std::string _optString; ///< Option string for `getopt` (e.g., "vha:"). + std::vector _options; ///< List of registered options. + std::string _optString; ///< Option string for `getopt` (e.g., "vha:"). /** * @brief Removes leading dashes from an option name. @@ -43,7 +43,7 @@ class GetoptArgumentParser : public BaseArgumentParser return stripped; } -public: + public: /** * @brief Adds an option to the parser. * @@ -114,7 +114,8 @@ class GetoptArgumentParser : public BaseArgumentParser int longindex = 0; int c = 0; - while ((c = getopt_long(argc, argv, _optString.c_str(), _longOptions.data(), &longindex)) != -1) + while ((c = getopt_long(argc, argv, _optString.c_str(), _longOptions.data(), &longindex)) != + -1) { if (c == '?') { @@ -129,7 +130,8 @@ class GetoptArgumentParser : public BaseArgumentParser for (auto& opt : _options) { - if (c == opt.shortName || (c != 0 && stripDashes(opt.name) == _longOptions[longindex].name)) + if (c == opt.shortName || + (c != 0 && stripDashes(opt.name) == _longOptions[longindex].name)) { opt.isSet = true; @@ -146,7 +148,8 @@ class GetoptArgumentParser : public BaseArgumentParser } else { - setError(ErrorCode::MISSING_ARGUMENT, "Missing argument for option: " + opt.name); + setError(ErrorCode::MISSING_ARGUMENT, + "Missing argument for option: " + opt.name); return false; } } @@ -169,7 +172,8 @@ class GetoptArgumentParser : public BaseArgumentParser for (const auto& opt : _options) { std::cout << opt.name << " -> " << opt.shortName << std::endl; - if (opt.name == name || (opt.shortName != '\0' && std::string(1, opt.shortName) == name)) + if (opt.name == name || + (opt.shortName != '\0' && std::string(1, opt.shortName) == name)) { return opt.isSet; } @@ -188,7 +192,8 @@ class GetoptArgumentParser : public BaseArgumentParser { for (const auto& opt : _options) { - if (opt.name == name || (opt.shortName != '\0' && std::string(1, opt.shortName) == name)) + if (opt.name == name || + (opt.shortName != '\0' && std::string(1, opt.shortName) == name)) { return opt.value; } diff --git a/include/ArgumentParser/IArgumentParser.hpp b/include/ArgumentParser/IArgumentParser.hpp index bbfca37..0a416b2 100644 --- a/include/ArgumentParser/IArgumentParser.hpp +++ b/include/ArgumentParser/IArgumentParser.hpp @@ -17,10 +17,10 @@ */ enum class ErrorCode : uint32_t { - SUCCESS = 0, ///< Parsing completed successfully. - INVALID_OPTION = 1, ///< An invalid option was provided. - MISSING_ARGUMENT = 2, ///< A required argument is missing. - UNKNOWN_ERROR = 3 ///< An unknown error occurred. + SUCCESS = 0, ///< Parsing completed successfully. + INVALID_OPTION = 1, ///< An invalid option was provided. + MISSING_ARGUMENT = 2, ///< A required argument is missing. + UNKNOWN_ERROR = 3 ///< An unknown error occurred. }; /** @@ -33,24 +33,24 @@ enum class ErrorCode : uint32_t */ class IArgumentParser { - public: - /** + public: + /** * @brief Virtual destructor for the interface. * * Ensures proper cleanup of derived classes. */ - virtual ~IArgumentParser() = default; + virtual ~IArgumentParser() = default; - /** + /** * @brief Adds an option to the parser. * * @param name The name of the option (e.g., "--option"). * @param hasArgument Indicates whether the option requires an argument. * @param shortName A single-character shorthand for the option (e.g., '-o'). */ - virtual void addOption(const std::string& name, bool hasArgument, char shortName) = 0; + virtual void addOption(const std::string& name, bool hasArgument, char shortName) = 0; - /** + /** * @brief Adds a flag to the parser. * * Flags are options that do not require arguments (e.g., "--verbose"). @@ -58,46 +58,46 @@ class IArgumentParser * @param name The name of the flag (e.g., "--flag"). * @param shortName A single-character shorthand for the flag (e.g., '-f'). */ - virtual void addFlag(const std::string& name, char shortName) = 0; + virtual void addFlag(const std::string& name, char shortName) = 0; - /** + /** * @brief Parses the command-line arguments. * * @param argc The number of arguments. * @param argv The array of argument strings. * @return `true` if parsing was successful, `false` otherwise. */ - virtual bool parse(int argc, char* argv[]) = 0; + virtual bool parse(int argc, char* argv[]) = 0; - /** + /** * @brief Checks if a specific option was provided. * * @param name The name of the option to check. * @return `true` if the option was provided, `false` otherwise. */ - virtual bool hasOption(const std::string& name) const = 0; + virtual bool hasOption(const std::string& name) const = 0; - /** + /** * @brief Retrieves the value of a specific option. * * @param name The name of the option. * @return The value of the option as a `std::string`. */ - virtual std::string getOptionValue(const std::string& name) const = 0; + virtual std::string getOptionValue(const std::string& name) const = 0; - /** + /** * @brief Retrieves the last error code. * * @return An `ErrorCode` representing the last error encountered. */ - virtual ErrorCode getLastError() const = 0; + virtual ErrorCode getLastError() const = 0; - /** + /** * @brief Retrieves the last error message. * * @return A `std::string` containing the error message. */ - virtual std::string getErrorMessage() const = 0; + virtual std::string getErrorMessage() const = 0; }; #endif // IARGUMENTPARSER_HPP diff --git a/include/ArgumentParser/OptionInfo.hpp b/include/ArgumentParser/OptionInfo.hpp index 5a25834..d3d693c 100644 --- a/include/ArgumentParser/OptionInfo.hpp +++ b/include/ArgumentParser/OptionInfo.hpp @@ -12,11 +12,11 @@ */ struct OptionInfo { - std::string name; ///< Long name of the option (e.g., "--verbose"). - char shortName; ///< Short name of the option (e.g., '-v'). - bool hasArgument; ///< Indicates whether the option requires an argument. - bool isSet; ///< Indicates whether the option was specified. - std::string value; ///< The value associated with the option (if `hasArgument` is `true`). + std::string name; ///< Long name of the option (e.g., "--verbose"). + char shortName; ///< Short name of the option (e.g., '-v'). + bool hasArgument; ///< Indicates whether the option requires an argument. + bool isSet; ///< Indicates whether the option was specified. + std::string value; ///< The value associated with the option (if `hasArgument` is `true`). }; #endif // OPTIONINFO_HPP diff --git a/include/Config/config.hpp b/include/Config/config.hpp index fd94090..36c7988 100644 --- a/include/Config/config.hpp +++ b/include/Config/config.hpp @@ -60,7 +60,7 @@ namespace ctrace */ struct FileConfig { - std::string src_file;///< Path to the source file. + std::string src_file; ///< Path to the source file. explicit FileConfig(std::string_view sv) : src_file(sv) {} explicit FileConfig(const std::string& str) : src_file(str) {} @@ -74,24 +74,25 @@ namespace ctrace */ struct GlobalConfig { - bool verbose = false; ///< Enables verbose output. + bool verbose = false; ///< Enables verbose output. std::launch hasAsync = std::launch::deferred; ///< Enables asynchronous execution. - bool hasSarifFormat = false; ///< Indicates if SARIF format is enabled. - bool hasStaticAnalysis = false; ///< Indicates if static analysis is enabled. - bool hasDynamicAnalysis = false; ///< Indicates if dynamic analysis is enabled. - bool hasInvokedSpecificTools = false; ///< Indicates if specific tools are invoked. - std::string ipc = ctrace_defs::IPC_TYPES.front(); ///< IPC method to use (e.g., fifo, socket). + bool hasSarifFormat = false; ///< Indicates if SARIF format is enabled. + bool hasStaticAnalysis = false; ///< Indicates if static analysis is enabled. + bool hasDynamicAnalysis = false; ///< Indicates if dynamic analysis is enabled. + bool hasInvokedSpecificTools = false; ///< Indicates if specific tools are invoked. + std::string ipc = + ctrace_defs::IPC_TYPES.front(); ///< IPC method to use (e.g., fifo, socket). std::string ipcPath = "/tmp/coretrace_ipc"; ///< Path for IPC communication. - std::string serverHost = "127.0.0.1"; ///< Host for server IPC (if applicable). - int serverPort = 8080; ///< Port for server IPC (if applicable). - std::string shutdownToken; ///< Token required for POST /shutdown. + std::string serverHost = "127.0.0.1"; ///< Host for server IPC (if applicable). + int serverPort = 8080; ///< Port for server IPC (if applicable). + std::string shutdownToken; ///< Token required for POST /shutdown. int shutdownTimeoutMs = 0; ///< Shutdown timeout in milliseconds (0 = wait indefinitely). std::vector specificTools; ///< List of specific tools to invoke. - std::string entry_points = "main"; ///< Entry points for analysis. + std::string entry_points = "main"; ///< Entry points for analysis. std::string report_file = "ctrace-report.txt"; ///< Path to the report file. - std::string output_file = "ctrace.out"; ///< Path to the output file. + std::string output_file = "ctrace.out"; ///< Path to the output file. }; /** @@ -102,7 +103,7 @@ namespace ctrace */ struct ProgramConfig { - GlobalConfig global; ///< Global configuration settings. + GlobalConfig global; ///< Global configuration settings. std::vector files; ///< List of file-specific configurations. /** @@ -130,140 +131,135 @@ namespace ctrace * The `ConfigProcessor` class maps command-line options to configuration * updates. It uses a command map to associate options with specific actions. */ - class ConfigProcessor { - public: - /** + class ConfigProcessor + { + public: + /** * @brief Constructs a `ConfigProcessor` with a reference to the program configuration. * * @param cfg The program configuration to update. */ - ConfigProcessor(ProgramConfig& cfg) : config(cfg) { - // Initialize command mappings. - commands["--help"] = [this](const std::string&) - { - printHelp(); - std::exit(0); - }; - commands["--verbose"] = [this](const std::string&) { config.global.verbose = true; }; - commands["--sarif-format"] = [this](const std::string&) { config.global.hasSarifFormat = true; }; - commands["--report-file"] = [this](const std::string& value) { config.global.report_file = value; }; - commands["--async"] = [this](const std::string&) { config.global.hasAsync = std::launch::async; std::cout << "Asynchronous execution enabled." << std::endl; }; - commands["--invoke"] = [this](const std::string& value) - { - config.global.hasInvokedSpecificTools = true; - auto parts = ctrace_tools::strings::splitByComma(value); + ConfigProcessor(ProgramConfig& cfg) : config(cfg) + { + // Initialize command mappings. + commands["--help"] = [this](const std::string&) + { + printHelp(); + std::exit(0); + }; + commands["--verbose"] = [this](const std::string&) { config.global.verbose = true; }; + commands["--sarif-format"] = [this](const std::string&) + { config.global.hasSarifFormat = true; }; + commands["--report-file"] = [this](const std::string& value) + { config.global.report_file = value; }; + commands["--async"] = [this](const std::string&) + { + config.global.hasAsync = std::launch::async; + std::cout << "Asynchronous execution enabled." << std::endl; + }; + commands["--invoke"] = [this](const std::string& value) + { + config.global.hasInvokedSpecificTools = true; + auto parts = ctrace_tools::strings::splitByComma(value); - for (const auto& part : parts) + for (const auto& part : parts) + { + // TODO: Refactor this block for better readability. + if (part == "flawfinder") { - // TODO: Refactor this block for better readability. - if (part == "flawfinder") - { - // config.global.hasStaticAnalysis = true; - config.global.specificTools.emplace_back("flawfinder"); - } - if (part == "ikos") - { - // config.global.hasStaticAnalysis = true; - config.global.specificTools.emplace_back("ikos"); - } - if (part == "cppcheck") - { - // config.global.hasStaticAnalysis = true; - config.global.specificTools.emplace_back("cppcheck"); - } - if (part == "tscancode") - { - // config.global.hasStaticAnalysis = true; - config.global.specificTools.emplace_back("tscancode"); - } - if (part == "ctrace_stack_analyzer") - { - // config.global.hasStaticAnalysis = true; - config.global.specificTools.emplace_back("ctrace_stack_analyzer"); - } + // config.global.hasStaticAnalysis = true; + config.global.specificTools.emplace_back("flawfinder"); } - }; - commands["--input"] = [this](const std::string& value) - { - config.addFile(value); - }; - commands["--static"] = [this](const std::string&) - { - config.global.hasStaticAnalysis = true; - }; - commands["--dyn"] = [this](const std::string&) - { - config.global.hasDynamicAnalysis = true; - }; - commands["--entry-points"] = [this](const std::string& value) - { - config.global.entry_points = value; - }; - commands["--ipc"] = [this](const std::string& value) - { - auto ipc_list = ctrace_defs::IPC_TYPES; - - if (std::find(ipc_list.begin(), ipc_list.end(), value) == ipc_list.end()) + if (part == "ikos") { - std::cerr << "Invalid IPC type: '" << value << "'\n" - << "Available IPC types: ["; - for (const auto& ipc : ipc_list) - { - std::cerr << ipc; - if (ipc != ipc_list.back()) - std::cerr << ", "; - } - std::cerr << "]" << std::endl; - std::exit(EXIT_FAILURE); + // config.global.hasStaticAnalysis = true; + config.global.specificTools.emplace_back("ikos"); } - config.global.ipc = value; - }; - commands["--ipc-path"] = [this](const std::string& value) - { - config.global.ipcPath = value; - }; - commands["--serve-host"] = [this](const std::string& value) - { - config.global.serverHost = value; - std::cout << "[DEBUG] Server host set to " << config.global.serverHost << std::endl; - }; - commands["--serve-port"] = [this](const std::string& value) - { - config.global.serverPort = std::stoi(value); - std::cout << "[DEBUG] Server port set to " << config.global.serverPort << std::endl; - }; - commands["--shutdown-token"] = [this](const std::string& value) - { - config.global.shutdownToken = value; - }; - commands["--shutdown-timeout-ms"] = [this](const std::string& value) + if (part == "cppcheck") + { + // config.global.hasStaticAnalysis = true; + config.global.specificTools.emplace_back("cppcheck"); + } + if (part == "tscancode") + { + // config.global.hasStaticAnalysis = true; + config.global.specificTools.emplace_back("tscancode"); + } + if (part == "ctrace_stack_analyzer") + { + // config.global.hasStaticAnalysis = true; + config.global.specificTools.emplace_back("ctrace_stack_analyzer"); + } + } + }; + commands["--input"] = [this](const std::string& value) { config.addFile(value); }; + commands["--static"] = [this](const std::string&) + { config.global.hasStaticAnalysis = true; }; + commands["--dyn"] = [this](const std::string&) + { config.global.hasDynamicAnalysis = true; }; + commands["--entry-points"] = [this](const std::string& value) + { config.global.entry_points = value; }; + commands["--ipc"] = [this](const std::string& value) + { + auto ipc_list = ctrace_defs::IPC_TYPES; + + if (std::find(ipc_list.begin(), ipc_list.end(), value) == ipc_list.end()) { - config.global.shutdownTimeoutMs = std::stoi(value); - if (config.global.shutdownTimeoutMs < 0) + std::cerr << "Invalid IPC type: '" << value << "'\n" + << "Available IPC types: ["; + for (const auto& ipc : ipc_list) { - config.global.shutdownTimeoutMs = 0; + std::cerr << ipc; + if (ipc != ipc_list.back()) + std::cerr << ", "; } - }; - // commands["--output"] = [this](const std::string& value) { - // if (!config.files.empty()) config.files.back().output_file = value; - // }; - } + std::cerr << "]" << std::endl; + std::exit(EXIT_FAILURE); + } + config.global.ipc = value; + }; + commands["--ipc-path"] = [this](const std::string& value) + { config.global.ipcPath = value; }; + commands["--serve-host"] = [this](const std::string& value) + { + config.global.serverHost = value; + std::cout << "[DEBUG] Server host set to " << config.global.serverHost << std::endl; + }; + commands["--serve-port"] = [this](const std::string& value) + { + config.global.serverPort = std::stoi(value); + std::cout << "[DEBUG] Server port set to " << config.global.serverPort << std::endl; + }; + commands["--shutdown-token"] = [this](const std::string& value) + { config.global.shutdownToken = value; }; + commands["--shutdown-timeout-ms"] = [this](const std::string& value) + { + config.global.shutdownTimeoutMs = std::stoi(value); + if (config.global.shutdownTimeoutMs < 0) + { + config.global.shutdownTimeoutMs = 0; + } + }; + // commands["--output"] = [this](const std::string& value) { + // if (!config.files.empty()) config.files.back().output_file = value; + // }; + } - /** + /** * @brief Executes a command based on the given option and value. * * @param option The command-line option. * @param value The value associated with the option. */ - void execute(const std::string& option, const std::string& value) + void execute(const std::string& option, const std::string& value) + { + if (commands.count(option)) { - if (commands.count(option)) - { - commands[option](value); - } + commands[option](value); } + } - /** + /** * @brief Processes all options from the argument manager. * * This function iterates through the available commands and applies @@ -271,21 +267,22 @@ namespace ctrace * * @param argManager The argument manager containing parsed options. */ - void process(ArgumentManager& argManager) + void process(ArgumentManager& argManager) + { + for (const auto& [option, command] : commands) { - for (const auto& [option, command] : commands) + if (argManager.hasOption(option)) { - if (argManager.hasOption(option)) - { - command(argManager.getOptionValue(option)); - } + command(argManager.getOptionValue(option)); } } + } - private: - ProgramConfig& config; ///< Reference to the program configuration. - std::unordered_map> commands; ///< Command map. + private: + ProgramConfig& config; ///< Reference to the program configuration. + std::unordered_map> + commands; ///< Command map. }; -} +} // namespace ctrace #endif // CONFIG_HPP diff --git a/include/Process/Ipc/HttpServer.hpp b/include/Process/Ipc/HttpServer.hpp index a48be8a..5e999ee 100644 --- a/include/Process/Ipc/HttpServer.hpp +++ b/include/Process/Ipc/HttpServer.hpp @@ -11,8 +11,8 @@ #include #include -#include "httplib.h" // cpp-httplib (header-only) -#include // nlohmann::json (header-only) +#include "httplib.h" // cpp-httplib (header-only) +#include // nlohmann::json (header-only) #include "Config/config.hpp" #include "Process/Tools/ToolsInvoker.hpp" @@ -26,34 +26,34 @@ using json = nlohmann::json; class ILogger { - public: - virtual ~ILogger() = default; - virtual void info(const std::string& msg) = 0; - virtual void error(const std::string& msg) = 0; + public: + virtual ~ILogger() = default; + virtual void info(const std::string& msg) = 0; + virtual void error(const std::string& msg) = 0; }; class ConsoleLogger : public ILogger { - public: - void info(const std::string& msg) override - { - std::cout << "[INFO] :: " << msg << '\n'; - } + public: + void info(const std::string& msg) override + { + std::cout << "[INFO] :: " << msg << '\n'; + } - void error(const std::string& msg) override - { - std::cerr << "[ERROR] :: " << msg << '\n'; - } + void error(const std::string& msg) override + { + std::cerr << "[ERROR] :: " << msg << '\n'; + } - void debug(const std::string& msg) - { - std::cout << "[DEBUG] :: " << msg << '\n'; - } + void debug(const std::string& msg) + { + std::cout << "[DEBUG] :: " << msg << '\n'; + } - void warn(const std::string& msg) - { - std::cout << "[WARN] :: " << msg << '\n'; - } + void warn(const std::string& msg) + { + std::cout << "[WARN] :: " << msg << '\n'; + } }; // ============================================================================ @@ -62,10 +62,8 @@ class ConsoleLogger : public ILogger class ApiHandler { -public: - explicit ApiHandler(ILogger& logger) - : logger_(logger) - {} + public: + explicit ApiHandler(ILogger& logger) : logger_(logger) {} json handle_request(const json& request) { @@ -73,26 +71,24 @@ class ApiHandler json response; response["proto"] = request.value("proto", std::string("coretrace-1.0")); - response["id"] = request.value("id", 0); - response["type"] = "response"; + response["id"] = request.value("id", 0); + response["type"] = "response"; std::string method = request.value("method", ""); - json params = request.value("params", json::object()); + json params = request.value("params", json::object()); - if (method == "run_analysis") { + if (method == "run_analysis") + { return handle_run_analysis(response, params); } // Méthode inconnue response["status"] = "error"; - response["error"] = { - {"code", "UnknownMethod"}, - {"message", "Unknown method: " + method} - }; + response["error"] = {{"code", "UnknownMethod"}, {"message", "Unknown method: " + method}}; return response; } -private: + private: struct ParseError { std::string code; @@ -150,7 +146,8 @@ class ApiHandler return true; } - static bool read_string_list(const json& params, const char* key, std::vector& out, ParseError& err) + static bool read_string_list(const json& params, const char* key, std::vector& out, + ParseError& err) { const auto it = params.find(key); if (it == params.end() || it->is_null()) @@ -182,7 +179,8 @@ class ApiHandler { if (!item.is_string()) { - err = {"InvalidParams", std::string("Expected string values in '") + key + "' array."}; + err = {"InvalidParams", + std::string("Expected string values in '") + key + "' array."}; return false; } const std::string value = item.get(); @@ -194,7 +192,8 @@ class ApiHandler return true; } - static bool apply_bool_fields(const json& params, ParseError& err, std::initializer_list fields) + static bool apply_bool_fields(const json& params, ParseError& err, + std::initializer_list fields) { for (const auto& field : fields) { @@ -206,7 +205,8 @@ class ApiHandler return true; } - static bool apply_string_fields(const json& params, ParseError& err, std::initializer_list fields) + static bool apply_string_fields(const json& params, ParseError& err, + std::initializer_list fields) { for (const auto& field : fields) { @@ -219,7 +219,8 @@ class ApiHandler } template - static bool apply_list_param(const json& params, const char* key, ParseError& err, ApplyFn&& apply) + static bool apply_list_param(const json& params, const char* key, ParseError& err, + ApplyFn&& apply) { std::vector values; if (!read_string_list(params, key, values, err)) @@ -247,7 +248,8 @@ class ApiHandler return joined; } - static bool apply_async_field(const json& params, ctrace::ProgramConfig& config, ParseError& err) + static bool apply_async_field(const json& params, ctrace::ProgramConfig& config, + ParseError& err) { bool async_enabled = false; if (!read_bool(params, "async", async_enabled, err)) @@ -278,10 +280,9 @@ class ApiHandler const auto& ipc_list = ctrace_defs::IPC_TYPES; if (std::find(ipc_list.begin(), ipc_list.end(), ipc_value) == ipc_list.end()) { - err = { - "InvalidParams", - "Invalid IPC type: '" + ipc_value + "'. Available IPC types: [" + join_with_comma(ipc_list) + "]" - }; + err = {"InvalidParams", "Invalid IPC type: '" + ipc_value + + "'. Available IPC types: [" + join_with_comma(ipc_list) + + "]"}; return false; } if (ipc_value == "serve") @@ -294,7 +295,8 @@ class ApiHandler return true; } - static bool build_config_from_params(const json& params, ctrace::ProgramConfig& config, ParseError& err) + static bool build_config_from_params(const json& params, ctrace::ProgramConfig& config, + ParseError& err) { if (!params.is_object()) { @@ -302,12 +304,13 @@ class ApiHandler return false; } - if (!apply_bool_fields(params, err, { - {"verbose", &config.global.verbose}, - {"sarif_format", &config.global.hasSarifFormat}, - {"static_analysis", &config.global.hasStaticAnalysis}, - {"dynamic_analysis", &config.global.hasDynamicAnalysis}, - })) + if (!apply_bool_fields(params, err, + { + {"verbose", &config.global.verbose}, + {"sarif_format", &config.global.hasSarifFormat}, + {"static_analysis", &config.global.hasStaticAnalysis}, + {"dynamic_analysis", &config.global.hasDynamicAnalysis}, + })) { return false; } @@ -315,36 +318,41 @@ class ApiHandler { return false; } - if (!apply_string_fields(params, err, { - {"report_file", &config.global.report_file}, - {"output_file", &config.global.output_file}, - {"ipc_path", &config.global.ipcPath}, - })) + if (!apply_string_fields(params, err, + { + {"report_file", &config.global.report_file}, + {"output_file", &config.global.output_file}, + {"ipc_path", &config.global.ipcPath}, + })) { return false; } - if (!apply_list_param(params, "entry_points", err, [&](const std::vector& values) { - config.global.entry_points = join_with_comma(values); - })) + if (!apply_list_param(params, "entry_points", err, + [&](const std::vector& values) + { config.global.entry_points = join_with_comma(values); })) { return false; } - if (!apply_list_param(params, "invoke", err, [&](const std::vector& values) { - config.global.hasInvokedSpecificTools = true; - config.global.specificTools = values; - })) + if (!apply_list_param(params, "invoke", err, + [&](const std::vector& values) + { + config.global.hasInvokedSpecificTools = true; + config.global.specificTools = values; + })) { return false; } - if (!apply_list_param(params, "input", err, [&](const std::vector& values) { - for (const auto& file : values) - { - if (!file.empty()) - { - config.addFile(file); - } - } - })) + if (!apply_list_param(params, "input", err, + [&](const std::vector& values) + { + for (const auto& file : values) + { + if (!file.empty()) + { + config.addFile(file); + } + } + })) { return false; } @@ -356,16 +364,19 @@ class ApiHandler return true; } - static bool run_analysis(const ctrace::ProgramConfig& config, ILogger& logger, json& result, ParseError& err) + static bool run_analysis(const ctrace::ProgramConfig& config, ILogger& logger, json& result, + ParseError& err) { if (config.files.empty()) { err = {"MissingInput", "Input files are required for analysis."}; return false; } - if (!config.global.hasStaticAnalysis && !config.global.hasDynamicAnalysis && !config.global.hasInvokedSpecificTools) + if (!config.global.hasStaticAnalysis && !config.global.hasDynamicAnalysis && + !config.global.hasInvokedSpecificTools) { - err = {"NoAnalysisSelected", "Enable static_analysis, dynamic_analysis, or invoke tools."}; + err = {"NoAnalysisSelected", + "Enable static_analysis, dynamic_analysis, or invoke tools."}; return false; } @@ -467,10 +478,7 @@ class ApiHandler if (!build_config_from_params(params, config, err)) { baseResponse["status"] = "error"; - baseResponse["error"] = { - {"code", err.code}, - {"message", err.message} - }; + baseResponse["error"] = {{"code", err.code}, {"message", err.message}}; return baseResponse; } @@ -478,10 +486,7 @@ class ApiHandler if (!run_analysis(config, logger_, result, err)) { baseResponse["status"] = "error"; - baseResponse["error"] = { - {"code", err.code}, - {"message", err.message} - }; + baseResponse["error"] = {{"code", err.code}, {"message", err.message}}; return baseResponse; } @@ -497,266 +502,268 @@ class ApiHandler class HttpServer { - public: - HttpServer(ApiHandler& apiHandler, ILogger& logger, const ctrace::GlobalConfig& config) - : apiHandler_(apiHandler), - logger_(logger), - shutdown_token_(config.shutdownToken), - shutdown_timeout_(std::chrono::milliseconds(config.shutdownTimeoutMs)) - {} - - void run(const std::string& host, int port) - { - // CORS - server_.Options("/api", [this](const httplib::Request&, httplib::Response& res) { - set_cors(res); - if (is_shutting_down()) - { - res.status = 503; - } - else - { - res.status = 200; - } - }); - - // Endpoint principal - server_.Post("/api", [this](const httplib::Request& req, httplib::Response& res) { - set_cors(res); - handle_post_api(req, res); - }); - - // Shutdown endpoint - server_.Options("/shutdown", [this](const httplib::Request&, httplib::Response& res) { - set_cors(res); - if (is_shutting_down()) - { - res.status = 503; - } - else - { - res.status = 200; - } - }); + public: + HttpServer(ApiHandler& apiHandler, ILogger& logger, const ctrace::GlobalConfig& config) + : apiHandler_(apiHandler), logger_(logger), shutdown_token_(config.shutdownToken), + shutdown_timeout_(std::chrono::milliseconds(config.shutdownTimeoutMs)) + { + } - server_.Post("/shutdown", [this](const httplib::Request& req, httplib::Response& res) { - set_cors(res); - handle_post_shutdown(req, res); - }); + void run(const std::string& host, int port) + { + // CORS + server_.Options("/api", + [this](const httplib::Request&, httplib::Response& res) + { + set_cors(res); + if (is_shutting_down()) + { + res.status = 503; + } + else + { + res.status = 200; + } + }); + + // Endpoint principal + server_.Post("/api", + [this](const httplib::Request& req, httplib::Response& res) + { + set_cors(res); + handle_post_api(req, res); + }); + + // Shutdown endpoint + server_.Options("/shutdown", + [this](const httplib::Request&, httplib::Response& res) + { + set_cors(res); + if (is_shutting_down()) + { + res.status = 503; + } + else + { + res.status = 200; + } + }); + + server_.Post("/shutdown", + [this](const httplib::Request& req, httplib::Response& res) + { + set_cors(res); + handle_post_shutdown(req, res); + }); + + logger_.info("[SERVER] Listening on http://" + host + ":" + std::to_string(port)); + server_.listen(host.c_str(), port); + finalize_shutdown(); + } - logger_.info("[SERVER] Listening on http://" + host + ":" + std::to_string(port)); - server_.listen(host.c_str(), port); - finalize_shutdown(); + private: + struct InFlightGuard + { + explicit InFlightGuard(HttpServer& server) : server_(server) + { + server_.begin_request(); } - private: - struct InFlightGuard + ~InFlightGuard() { - explicit InFlightGuard(HttpServer& server) : server_(server) - { - server_.begin_request(); - } + server_.end_request(); + } - ~InFlightGuard() - { - server_.end_request(); - } + HttpServer& server_; + }; - HttpServer& server_; - }; + httplib::Server server_; + ApiHandler& apiHandler_; + ILogger& logger_; + std::atomic shutting_down_{false}; + std::atomic shutdown_requested_{false}; + std::atomic in_flight_{0}; + std::mutex shutdown_mutex_; + std::condition_variable shutdown_cv_; + std::thread shutdown_thread_; + std::string shutdown_token_; + std::chrono::milliseconds shutdown_timeout_{0}; + + bool is_shutting_down() const + { + return shutting_down_.load(std::memory_order_acquire); + } - httplib::Server server_; - ApiHandler& apiHandler_; - ILogger& logger_; - std::atomic shutting_down_{false}; - std::atomic shutdown_requested_{false}; - std::atomic in_flight_{0}; - std::mutex shutdown_mutex_; - std::condition_variable shutdown_cv_; - std::thread shutdown_thread_; - std::string shutdown_token_; - std::chrono::milliseconds shutdown_timeout_{0}; + static void set_cors(httplib::Response& res) + { + res.set_header("Access-Control-Allow-Origin", "*"); + res.set_header("Access-Control-Allow-Methods", "POST, OPTIONS"); + res.set_header("Access-Control-Allow-Headers", "Content-Type"); + } - bool is_shutting_down() const - { - return shutting_down_.load(std::memory_order_acquire); - } + void begin_request() + { + in_flight_.fetch_add(1, std::memory_order_relaxed); + } - static void set_cors(httplib::Response& res) + void end_request() + { + const int remaining = in_flight_.fetch_sub(1, std::memory_order_acq_rel) - 1; + if (remaining == 0) { - res.set_header("Access-Control-Allow-Origin", "*"); - res.set_header("Access-Control-Allow-Methods", "POST, OPTIONS"); - res.set_header("Access-Control-Allow-Headers", "Content-Type"); + shutdown_cv_.notify_all(); } + } - void begin_request() + static void flush_logs() + { + std::cout << std::flush; + std::cerr << std::flush; + } + + bool is_authorized_shutdown(const httplib::Request& req) const + { + if (shutdown_token_.empty()) { - in_flight_.fetch_add(1, std::memory_order_relaxed); + return false; } - void end_request() + const std::string bearer = req.get_header_value("Authorization"); + if (!bearer.empty()) { - const int remaining = in_flight_.fetch_sub(1, std::memory_order_acq_rel) - 1; - if (remaining == 0) + const std::string prefix = "Bearer "; + if (bearer.rfind(prefix, 0) == 0) { - shutdown_cv_.notify_all(); + return bearer.substr(prefix.size()) == shutdown_token_; } + return bearer == shutdown_token_; } - static void flush_logs() - { - std::cout << std::flush; - std::cerr << std::flush; - } + const std::string admin_token = req.get_header_value("X-Admin-Token"); + return !admin_token.empty() && admin_token == shutdown_token_; + } - bool is_authorized_shutdown(const httplib::Request& req) const + void initiate_shutdown() + { + bool expected = false; + if (!shutdown_requested_.compare_exchange_strong(expected, true)) { - if (shutdown_token_.empty()) - { - return false; - } - - const std::string bearer = req.get_header_value("Authorization"); - if (!bearer.empty()) - { - const std::string prefix = "Bearer "; - if (bearer.rfind(prefix, 0) == 0) - { - return bearer.substr(prefix.size()) == shutdown_token_; - } - return bearer == shutdown_token_; - } - - const std::string admin_token = req.get_header_value("X-Admin-Token"); - return !admin_token.empty() && admin_token == shutdown_token_; + return; } - void initiate_shutdown() - { - bool expected = false; - if (!shutdown_requested_.compare_exchange_strong(expected, true)) - { - return; - } - - shutting_down_.store(true, std::memory_order_release); + shutting_down_.store(true, std::memory_order_release); - shutdown_thread_ = std::thread([this]() { + shutdown_thread_ = std::thread( + [this]() + { logger_.info("[SERVER] Shutdown requested. Stopping listener..."); server_.stop(); wait_for_inflight_or_timeout(); }); - } + } - void wait_for_inflight_or_timeout() - { - std::unique_lock lock(shutdown_mutex_); - const auto done = [this]() { - return in_flight_.load(std::memory_order_acquire) == 0; - }; + void wait_for_inflight_or_timeout() + { + std::unique_lock lock(shutdown_mutex_); + const auto done = [this]() { return in_flight_.load(std::memory_order_acquire) == 0; }; - if (shutdown_timeout_.count() > 0) - { - if (!shutdown_cv_.wait_for(lock, shutdown_timeout_, done)) - { - logger_.error("[SERVER] Shutdown timeout exceeded. Forcing exit."); - } - } - else + if (shutdown_timeout_.count() > 0) + { + if (!shutdown_cv_.wait_for(lock, shutdown_timeout_, done)) { - shutdown_cv_.wait(lock, done); + logger_.error("[SERVER] Shutdown timeout exceeded. Forcing exit."); } } - - void finalize_shutdown() + else { - if (shutdown_thread_.joinable()) - { - shutdown_thread_.join(); - } - if (shutdown_requested_.load(std::memory_order_acquire)) - { - logger_.info("[SERVER] Shutdown complete."); - } - flush_logs(); + shutdown_cv_.wait(lock, done); } + } - void handle_post_api(const httplib::Request& req, httplib::Response& res) + void finalize_shutdown() + { + if (shutdown_thread_.joinable()) { - if (is_shutting_down()) - { - json err; - err["proto"] = "coretrace-1.0"; - err["type"] = "response"; - err["status"] = "error"; - err["error"] = { - {"code", "ServerShuttingDown"}, - {"message", "Server is shutting down."} - }; - res.status = 503; - res.set_content(err.dump(), "application/json"); - return; - } + shutdown_thread_.join(); + } + if (shutdown_requested_.load(std::memory_order_acquire)) + { + logger_.info("[SERVER] Shutdown complete."); + } + flush_logs(); + } - InFlightGuard guard(*this); - try { - json request = json::parse(req.body); - json response = apiHandler_.handle_request(request); - - res.status = 200; - res.set_content(response.dump(), "application/json"); - } catch (const std::exception& e) { - logger_.error(std::string("Exception while handling /api: ") + e.what()); - - json err; - err["proto"] = "coretrace-1.0"; - err["type"] = "response"; - err["status"] = "error"; - err["error"] = { - {"code", "InvalidRequest"}, - {"message", e.what()} - }; - - res.status = 400; - res.set_content(err.dump(), "application/json"); - } + void handle_post_api(const httplib::Request& req, httplib::Response& res) + { + if (is_shutting_down()) + { + json err; + err["proto"] = "coretrace-1.0"; + err["type"] = "response"; + err["status"] = "error"; + err["error"] = {{"code", "ServerShuttingDown"}, + {"message", "Server is shutting down."}}; + res.status = 503; + res.set_content(err.dump(), "application/json"); + return; } - void handle_post_shutdown(const httplib::Request& req, httplib::Response& res) + InFlightGuard guard(*this); + try { - if (!is_authorized_shutdown(req)) - { - json err; - err["status"] = "error"; - err["error"] = { - {"code", "Unauthorized"}, - {"message", shutdown_token_.empty() - ? "Shutdown token not configured." - : "Invalid shutdown token."} - }; - res.status = 403; - res.set_content(err.dump(), "application/json"); - return; - } + json request = json::parse(req.body); + json response = apiHandler_.handle_request(request); - if (shutdown_requested_.load(std::memory_order_acquire)) - { - json ok; - ok["status"] = "accepted"; - ok["message"] = "Shutdown already in progress."; - ok["timeout_ms"] = shutdown_timeout_.count(); - res.status = 202; - res.set_content(ok.dump(), "application/json"); - return; - } + res.status = 200; + res.set_content(response.dump(), "application/json"); + } + catch (const std::exception& e) + { + logger_.error(std::string("Exception while handling /api: ") + e.what()); + + json err; + err["proto"] = "coretrace-1.0"; + err["type"] = "response"; + err["status"] = "error"; + err["error"] = {{"code", "InvalidRequest"}, {"message", e.what()}}; + + res.status = 400; + res.set_content(err.dump(), "application/json"); + } + } + void handle_post_shutdown(const httplib::Request& req, httplib::Response& res) + { + if (!is_authorized_shutdown(req)) + { + json err; + err["status"] = "error"; + err["error"] = {{"code", "Unauthorized"}, + {"message", shutdown_token_.empty() ? "Shutdown token not configured." + : "Invalid shutdown token."}}; + res.status = 403; + res.set_content(err.dump(), "application/json"); + return; + } + + if (shutdown_requested_.load(std::memory_order_acquire)) + { json ok; ok["status"] = "accepted"; - ok["message"] = "Shutdown initiated."; + ok["message"] = "Shutdown already in progress."; ok["timeout_ms"] = shutdown_timeout_.count(); res.status = 202; res.set_content(ok.dump(), "application/json"); - - initiate_shutdown(); + return; } + + json ok; + ok["status"] = "accepted"; + ok["message"] = "Shutdown initiated."; + ok["timeout_ms"] = shutdown_timeout_.count(); + res.status = 202; + res.set_content(ok.dump(), "application/json"); + + initiate_shutdown(); + } }; diff --git a/include/Process/Ipc/IpcStrategy.hpp b/include/Process/Ipc/IpcStrategy.hpp index 49eb9b1..917cf59 100644 --- a/include/Process/Ipc/IpcStrategy.hpp +++ b/include/Process/Ipc/IpcStrategy.hpp @@ -15,55 +15,67 @@ class IpcStrategy { -public: + public: virtual ~IpcStrategy() = default; virtual void write(const std::string& data) = 0; - virtual void close() = 0; // Pour cleanup RAII + virtual void close() = 0; // Pour cleanup RAII }; namespace ctrace::ipc { class SocketError : public std::runtime_error - { - public: + { + public: explicit SocketError(const std::string& msg) - : std::runtime_error("[IPC::SocketError] " + msg) {} + : std::runtime_error("[IPC::SocketError] " + msg) + { + } }; -} +} // namespace ctrace::ipc class UnixSocketStrategy : public IpcStrategy { -private: + private: int sock; std::string path; -public: - UnixSocketStrategy(const std::string& socketPath) : path(socketPath), sock(-1) { + + public: + UnixSocketStrategy(const std::string& socketPath) : path(socketPath), sock(-1) + { sock = socket(AF_UNIX, SOCK_STREAM, 0); - if (sock == -1) { + if (sock == -1) + { throw std::runtime_error("Error socket creation: " + std::string(strerror(errno))); } sockaddr_un addr; memset(&addr, 0, sizeof(addr)); addr.sun_family = AF_UNIX; strncpy(addr.sun_path, path.c_str(), sizeof(addr.sun_path) - 1); - if (connect(sock, (sockaddr*)&addr, sizeof(addr)) == -1) { + if (connect(sock, (sockaddr*)&addr, sizeof(addr)) == -1) + { throw std::runtime_error("Error socket connexion: " + std::string(strerror(errno))); } } - ~UnixSocketStrategy() { close(); } + ~UnixSocketStrategy() + { + close(); + } void write(const std::string& data) override { if (sock == -1) { - throw ctrace::ipc::SocketError(std::string("Socket is not connected: ") + strerror(errno)); + throw ctrace::ipc::SocketError(std::string("Socket is not connected: ") + + strerror(errno)); } if (send(sock, data.c_str(), data.size(), 0) == -1) { throw ctrace::ipc::SocketError(std::string("Connection failed: ") + strerror(errno)); } } - void close() override { - if (sock != -1) { + void close() override + { + if (sock != -1) + { ::close(sock); sock = -1; // unlink(path.c_str()); // Optionnel diff --git a/include/Process/LinuxProcess.hpp b/include/Process/LinuxProcess.hpp index ff574ae..f947a88 100644 --- a/include/Process/LinuxProcess.hpp +++ b/include/Process/LinuxProcess.hpp @@ -9,16 +9,17 @@ #include "Process.hpp" #include "ThreadProcess.hpp" #include - #include +#include -class UnixProcess : public Process { -public: +class UnixProcess : public Process +{ + public: UnixProcess(const std::string& command, std::vector args) : m_command(command) { m_arguments = args; } -protected: + protected: void prepare() override { ctrace::Thread::Output::cout("Preparing Unix/Linux process"); @@ -48,7 +49,8 @@ class UnixProcess : public Process { { std::vector execArgs; execArgs.push_back(const_cast(m_command.c_str())); - for (auto& arg : m_arguments) { + for (auto& arg : m_arguments) + { execArgs.push_back(const_cast(arg.c_str())); } execArgs.push_back(nullptr); @@ -59,7 +61,9 @@ class UnixProcess : public Process { execvp(m_command.c_str(), execArgs.data()); _exit(EXIT_FAILURE); - } else { + } + else + { close(logFd); int status; waitpid(pid, &status, 0); @@ -110,7 +114,7 @@ class UnixProcess : public Process { // std::cout << "Logs captured: " << logOutput << "\n"; } -private: + private: std::string m_command; std::string log_path_; }; diff --git a/include/Process/Process.hpp b/include/Process/Process.hpp index fd1cba9..329c251 100644 --- a/include/Process/Process.hpp +++ b/include/Process/Process.hpp @@ -5,28 +5,30 @@ #include #include -class Process { - public: - virtual ~Process() = default; - - void execute(void) - { - prepare(); - run(); - cleanup(); - } - - std::string logOutput; - protected: - virtual void prepare() = 0; - virtual void run() = 0; - virtual void cleanup() = 0; - - virtual void prepareArguments() = 0; - virtual void captureLogs() = 0; - - std::vector m_arguments; - std::stringstream log_buffer; +class Process +{ + public: + virtual ~Process() = default; + + void execute(void) + { + prepare(); + run(); + cleanup(); + } + + std::string logOutput; + + protected: + virtual void prepare() = 0; + virtual void run() = 0; + virtual void cleanup() = 0; + + virtual void prepareArguments() = 0; + virtual void captureLogs() = 0; + + std::vector m_arguments; + std::stringstream log_buffer; }; #endif // PROCESS_HPP diff --git a/include/Process/ProcessFactory.hpp b/include/Process/ProcessFactory.hpp index bfea77d..7a4805f 100644 --- a/include/Process/ProcessFactory.hpp +++ b/include/Process/ProcessFactory.hpp @@ -16,9 +16,10 @@ * This allows the application to abstract away platform-specific details when * managing processes. */ -class ProcessFactory { - public: - /** +class ProcessFactory +{ + public: + /** * @brief Creates a platform-specific `Process` instance. * * This method determines the current operating system at compile time @@ -31,20 +32,21 @@ class ProcessFactory { * @note The method uses preprocessor directives to select the appropriate * implementation for Windows, Linux, or macOS. */ - static std::unique_ptr createProcess(const std::string& command, const std::vector& args = {}) - { - #if defined(_WIN32) - ctrace::Thread::Output::cout("Creating Windows process for command: " + command); - return std::make_unique(command, args); - #elif defined(__linux__) - ctrace::Thread::Output::cout("Creating Linux process for command: " + command); - return std::make_unique(command, args); - #elif defined(__APPLE__) - ctrace::Thread::Output::cout("Creating macOS process for command: " + command); - // return std::make_unique(command, args); // doesn't work with tscancode - return std::make_unique(command, args); - #endif - } + static std::unique_ptr createProcess(const std::string& command, + const std::vector& args = {}) + { +#if defined(_WIN32) + ctrace::Thread::Output::cout("Creating Windows process for command: " + command); + return std::make_unique(command, args); +#elif defined(__linux__) + ctrace::Thread::Output::cout("Creating Linux process for command: " + command); + return std::make_unique(command, args); +#elif defined(__APPLE__) + ctrace::Thread::Output::cout("Creating macOS process for command: " + command); + // return std::make_unique(command, args); // doesn't work with tscancode + return std::make_unique(command, args); +#endif + } }; #endif // PROCESSFACTORY_HPP diff --git a/include/Process/ThreadProcess.hpp b/include/Process/ThreadProcess.hpp index 131a702..fac109f 100644 --- a/include/Process/ThreadProcess.hpp +++ b/include/Process/ThreadProcess.hpp @@ -22,22 +22,23 @@ namespace ctrace class CaptureBuffer { - public: - void append(const std::string& tool, const std::string& stream, const std::string& message) - { - std::lock_guard lock(mutex_); - lines_[tool].push_back({stream, message}); - } + public: + void append(const std::string& tool, const std::string& stream, + const std::string& message) + { + std::lock_guard lock(mutex_); + lines_[tool].push_back({stream, message}); + } - std::unordered_map> snapshot() const - { - std::lock_guard lock(mutex_); - return lines_; - } + std::unordered_map> snapshot() const + { + std::lock_guard lock(mutex_); + return lines_; + } - private: - mutable std::mutex mutex_; - std::unordered_map> lines_; + private: + mutable std::mutex mutex_; + std::unordered_map> lines_; }; struct CaptureContext @@ -52,33 +53,31 @@ namespace ctrace class ScopedCapture { - public: - explicit ScopedCapture(const CaptureContext* ctx) - : previous_(capture_context), active_(ctx != nullptr) + public: + explicit ScopedCapture(const CaptureContext* ctx) + : previous_(capture_context), active_(ctx != nullptr) + { + if (active_) { - if (active_) - { - capture_context = ctx; - } + capture_context = ctx; } + } - ~ScopedCapture() + ~ScopedCapture() + { + if (active_) { - if (active_) - { - capture_context = previous_; - } + capture_context = previous_; } + } - private: - const CaptureContext* previous_; - bool active_; + private: + const CaptureContext* previous_; + bool active_; }; - static void emit_line(const std::string& stream, - const std::string& message, - std::ostream& target, - bool capture_line) + static void emit_line(const std::string& stream, const std::string& message, + std::ostream& target, bool capture_line) { const CaptureContext* ctx = capture_context; if (capture_line && ctx && ctx->buffer) @@ -112,8 +111,8 @@ namespace ctrace { emit_line("stderr", message, std::cerr, true); } - } - } -} + } // namespace Output + } // namespace Thread +} // namespace ctrace #endif // THREAD_PROCESS_HPP diff --git a/include/Process/Tools/AnalysisTools.hpp b/include/Process/Tools/AnalysisTools.hpp index a0db540..c4cc222 100644 --- a/include/Process/Tools/AnalysisTools.hpp +++ b/include/Process/Tools/AnalysisTools.hpp @@ -20,70 +20,71 @@ using json = nlohmann::json; class EntryPoint { - public: - EntryPoint(const std::string& entryPointName, const std::vector& paramTypes) - : name(entryPointName), paramTypes(paramTypes) - { - m_isMangled = ctrace_tools::mangle::isMangled(name); + public: + EntryPoint(const std::string& entryPointName, const std::vector& paramTypes) + : name(entryPointName), paramTypes(paramTypes) + { + m_isMangled = ctrace_tools::mangle::isMangled(name); - if (m_isMangled) - { - mangledName = name; - } - else - { - mangledName = ctrace_tools::mangle::mangleFunction("", name, paramTypes); - } + if (m_isMangled) + { + mangledName = name; } - ~EntryPoint() = default; - [[nodiscard]] std::string_view getEntryPointNameCMode() const noexcept + else { - if (name.empty()) - { - return "unnamed"; - } - return name; + mangledName = ctrace_tools::mangle::mangleFunction("", name, paramTypes); } - [[nodiscard]] std::string_view getEntryPointNameCCMode() const noexcept + } + ~EntryPoint() = default; + [[nodiscard]] std::string_view getEntryPointNameCMode() const noexcept + { + if (name.empty()) { - if (mangledName.empty()) - { - return "unnamed"; - } - return getMangledName(); + return "unnamed"; } - [[nodiscard]] std::string_view getMangledName() const noexcept + return name; + } + [[nodiscard]] std::string_view getEntryPointNameCCMode() const noexcept + { + if (mangledName.empty()) { - if (mangledName.empty()) - { - return "unnamed"; - } - return mangledName; + return "unnamed"; } - [[nodiscard]] std::vector getParamTypes() const noexcept + return getMangledName(); + } + [[nodiscard]] std::string_view getMangledName() const noexcept + { + if (mangledName.empty()) { - if (paramTypes.empty()) - { - return {"void"}; - } - return paramTypes; + return "unnamed"; } - protected: - std::string name; - std::string mangledName; - std::vector paramTypes; - private: - bool m_isMangled = false; -}; + return mangledName; + } + [[nodiscard]] std::vector getParamTypes() const noexcept + { + if (paramTypes.empty()) + { + return {"void"}; + } + return paramTypes; + } + + protected: + std::string name; + std::string mangledName; + std::vector paramTypes; + private: + bool m_isMangled = false; +}; namespace ctrace { -// Static analysis tools -class IkosToolImplementation : public AnalysisToolBase -{ - public: + // Static analysis tools + class IkosToolImplementation : public AnalysisToolBase + { + public: void execute(const std::string& file, ctrace::ProgramConfig config) const override { ctrace::Thread::Output::cout("Running IKOS on " + file); @@ -91,8 +92,8 @@ class IkosToolImplementation : public AnalysisToolBase std::string entry_points = config.global.entry_points; std::string report_file = config.global.report_file; - - try { + try + { std::vector argsProcess; if (config.global.hasSarifFormat) @@ -111,7 +112,8 @@ class IkosToolImplementation : public AnalysisToolBase // std::cout << "C file detected\n"; ctrace::Thread::Output::cout("C file detected"); EntryPoint entryPoint(entry_points, {"void"}); // TODO parse function parameters - ctrace::Thread::Output::cout("Entry point: " + std::string(entryPoint.getEntryPointNameCMode())); + ctrace::Thread::Output::cout("Entry point: " + + std::string(entryPoint.getEntryPointNameCMode())); std::string arg = "--entry-points="; arg += entryPoint.getEntryPointNameCMode(); argsProcess.push_back(arg); @@ -120,7 +122,8 @@ class IkosToolImplementation : public AnalysisToolBase { ctrace::Thread::Output::cout("C++ file detected"); EntryPoint entryPoint(entry_points, {"void"}); // TODO parse function parameters - ctrace::Thread::Output::cout("Entry point: " + std::string(entryPoint.getEntryPointNameCCMode())); + ctrace::Thread::Output::cout("Entry point: " + + std::string(entryPoint.getEntryPointNameCCMode())); std::string arg = "--entry-points="; arg += entryPoint.getEntryPointNameCCMode(); argsProcess.push_back(arg); @@ -128,12 +131,14 @@ class IkosToolImplementation : public AnalysisToolBase argsProcess.push_back("--report-file=" + report_file); argsProcess.push_back(src_file); - auto process = ProcessFactory::createProcess("./ikos/src/ikos-build/bin/ikos", argsProcess); // ou "cmd.exe" pour Windows + auto process = ProcessFactory::createProcess( + "./ikos/src/ikos-build/bin/ikos", argsProcess); // ou "cmd.exe" pour Windows // std::this_thread::sleep_for(std::chrono::seconds(5)); process->execute(); ctrace::Thread::Output::tool_out(process->logOutput); - - } catch (const std::exception& e) { + } + catch (const std::exception& e) + { ctrace::Thread::Output::tool_err("Error: " + std::string(e.what())); // return 1; } @@ -142,18 +147,18 @@ class IkosToolImplementation : public AnalysisToolBase { return "ikos"; } -}; + }; -class StackAnalyzerToolImplementation : public AnalysisToolBase -{ - public: + class StackAnalyzerToolImplementation : public AnalysisToolBase + { + public: void execute(const std::string& file, ctrace::ProgramConfig config) const override; std::string name() const override; -}; + }; -class FlawfinderToolImplementation : public AnalysisToolBase -{ - public: + class FlawfinderToolImplementation : public AnalysisToolBase + { + public: void execute(const std::string& file, ctrace::ProgramConfig config) const override { ctrace::Thread::Output::cout("Running flawfinder on " + file); @@ -162,7 +167,8 @@ class FlawfinderToolImplementation : public AnalysisToolBase std::string src_file = file; std::string entry_points = config.global.entry_points; - try { + try + { std::vector argsProcess; // = {"flawfinder.py", "-F", "-c", "-C", "-D", "main.c"}; argsProcess.push_back("./flawfinder/src/flawfinder-build/flawfinder.py"); @@ -175,7 +181,8 @@ class FlawfinderToolImplementation : public AnalysisToolBase argsProcess.push_back("--sarif"); } argsProcess.push_back(src_file); - auto process = ProcessFactory::createProcess("python3", argsProcess); // or "cmd.exe" for Windows + auto process = ProcessFactory::createProcess( + "python3", argsProcess); // or "cmd.exe" for Windows process->execute(); if (config.global.ipc == "standardIO") @@ -186,8 +193,9 @@ class FlawfinderToolImplementation : public AnalysisToolBase { ipc->write(process->logOutput); } - - } catch (const std::exception& e) { + } + catch (const std::exception& e) + { ctrace::Thread::Output::tool_err("Error: " + std::string(e.what())); return; } @@ -196,24 +204,23 @@ class FlawfinderToolImplementation : public AnalysisToolBase { return "flawfinder"; } -}; + }; -class TscancodeToolImplementation : public AnalysisToolBase -{ -public: - void execute(const std::string& file, ProgramConfig config) const override; - std::string name() const override; - -protected: - std::string_view severityToLevel(const std::string& severity) const; - json sarifFormat(const std::string &buffer, const std::string &outputFile) const; - json jsonFormat(const std::string &buffer, const std::string &outputFile) const; -}; + class TscancodeToolImplementation : public AnalysisToolBase + { + public: + void execute(const std::string& file, ProgramConfig config) const override; + std::string name() const override; + protected: + std::string_view severityToLevel(const std::string& severity) const; + json sarifFormat(const std::string& buffer, const std::string& outputFile) const; + json jsonFormat(const std::string& buffer, const std::string& outputFile) const; + }; -class CppCheckToolImplementation : public AnalysisToolBase -{ - public: + class CppCheckToolImplementation : public AnalysisToolBase + { + public: void execute(const std::string& file, ctrace::ProgramConfig config) const override { ctrace::Thread::Output::cout("Running ikos on " + file); @@ -221,7 +228,8 @@ class CppCheckToolImplementation : public AnalysisToolBase std::string src_file = file; std::string entry_points = config.global.entry_points; - try { + try + { std::vector argsProcess; if (has_sarif_format) @@ -231,10 +239,13 @@ class CppCheckToolImplementation : public AnalysisToolBase // argsProcess.push_back("--enable=all"); argsProcess.push_back(src_file); - auto process = ProcessFactory::createProcess("/opt/homebrew/bin/cppcheck", argsProcess); // ou "cmd.exe" pour Windows + auto process = ProcessFactory::createProcess( + "/opt/homebrew/bin/cppcheck", argsProcess); // ou "cmd.exe" pour Windows process->execute(); ctrace::Thread::Output::tool_out(process->logOutput); - } catch (const std::exception& e) { + } + catch (const std::exception& e) + { ctrace::Thread::Output::tool_err("Error: " + std::string(e.what())); return; } @@ -243,12 +254,12 @@ class CppCheckToolImplementation : public AnalysisToolBase { return "ikos"; } -}; + }; // Outils dynamiques -class DynTool1 : public AnalysisToolBase -{ - public: + class DynTool1 : public AnalysisToolBase + { + public: void execute(const std::string& file, ctrace::ProgramConfig config) const override { ctrace::Thread::Output::cout("Running dyn_tools_1 on " + file); @@ -257,11 +268,11 @@ class DynTool1 : public AnalysisToolBase { return "dyn_tools_1"; } -}; + }; -class DynTool2 : public AnalysisToolBase -{ - public: + class DynTool2 : public AnalysisToolBase + { + public: void execute(const std::string& file, ctrace::ProgramConfig config) const override { ctrace::Thread::Output::cout("Running dyn_tools_2 on " + file); @@ -270,11 +281,11 @@ class DynTool2 : public AnalysisToolBase { return "dyn_tools_2"; } -}; + }; -class DynTool3 : public AnalysisToolBase -{ - public: + class DynTool3 : public AnalysisToolBase + { + public: void execute(const std::string& file, ctrace::ProgramConfig config) const override { ctrace::Thread::Output::cout("Running dyn_tools_3 on " + file); @@ -283,8 +294,8 @@ class DynTool3 : public AnalysisToolBase { return "dyn_tools_3"; } -}; + }; -} +} // namespace ctrace #endif // ANALYSIS_TOOLS_HPP diff --git a/include/Process/Tools/AnalysisToolsBase.hpp b/include/Process/Tools/AnalysisToolsBase.hpp index 025e457..f07c896 100644 --- a/include/Process/Tools/AnalysisToolsBase.hpp +++ b/include/Process/Tools/AnalysisToolsBase.hpp @@ -6,12 +6,14 @@ namespace ctrace class AnalysisToolBase : public IAnalysisTool { - protected: + protected: std::shared_ptr ipc; - public: - void setIpcStrategy(std::shared_ptr strategy) override { + + public: + void setIpcStrategy(std::shared_ptr strategy) override + { ipc = std::move(strategy); } }; -} +} // namespace ctrace diff --git a/include/Process/Tools/IAnalysisTools.hpp b/include/Process/Tools/IAnalysisTools.hpp index be78fa8..54ed634 100644 --- a/include/Process/Tools/IAnalysisTools.hpp +++ b/include/Process/Tools/IAnalysisTools.hpp @@ -9,7 +9,8 @@ class IpcStrategy; -namespace ctrace { +namespace ctrace +{ /** * @brief Interface for an analysis tool (Strategy pattern). @@ -18,16 +19,17 @@ namespace ctrace { * It follows the Strategy design pattern, allowing different tools to be * implemented and used interchangeably. */ - class IAnalysisTool { - public: - /** + class IAnalysisTool + { + public: + /** * @brief Virtual destructor for the interface. * * Ensures proper cleanup of derived classes. */ - virtual ~IAnalysisTool() = default; + virtual ~IAnalysisTool() = default; - /** + /** * @brief Executes the analysis tool on a given file. * * This method runs the analysis tool on the specified file using the @@ -36,24 +38,24 @@ namespace ctrace { * @param file The path to the file to analyze. * @param config The program configuration to use during the analysis. */ - virtual void execute(const std::string& file, ctrace::ProgramConfig config) const = 0; + virtual void execute(const std::string& file, ctrace::ProgramConfig config) const = 0; - /** + /** * @brief Retrieves the name of the analysis tool. * * @return A `std::string` representing the name of the tool. */ - virtual std::string name() const = 0; + virtual std::string name() const = 0; - /** + /** * @brief Sets the IPC strategy for the analysis tool. * This method allows the tool to communicate results or data * through the specified IPC mechanism. * @param ipc A shared pointer to an `IpcStrategy` instance. */ - virtual void setIpcStrategy(std::shared_ptr ipc) = 0; + virtual void setIpcStrategy(std::shared_ptr ipc) = 0; }; -} +} // namespace ctrace #endif // IANALYSISTOOLS_HPP diff --git a/include/Process/Tools/ToolsInvoker.hpp b/include/Process/Tools/ToolsInvoker.hpp index 9bc4a21..857eb06 100644 --- a/include/Process/Tools/ToolsInvoker.hpp +++ b/include/Process/Tools/ToolsInvoker.hpp @@ -19,55 +19,64 @@ #include #include -class ThreadPool { -public: +class ThreadPool +{ + public: ThreadPool(size_t numThreads) { - for (size_t i = 0; i < numThreads; ++i) { - workers.emplace_back([this] { - while (true) { - std::function task; + for (size_t i = 0; i < numThreads; ++i) + { + workers.emplace_back( + [this] + { + while (true) { - std::unique_lock lock(queueMutex); - condition.wait(lock, [this] { return !tasks.empty() || stop; }); - if (stop && tasks.empty()) return; - task = std::move(tasks.front()); - tasks.pop(); + std::function task; + { + std::unique_lock lock(queueMutex); + condition.wait(lock, [this] { return !tasks.empty() || stop; }); + if (stop && tasks.empty()) + return; + task = std::move(tasks.front()); + tasks.pop(); + } + task(); } - task(); - } - }); + }); } } - ~ThreadPool() { + ~ThreadPool() + { { std::unique_lock lock(queueMutex); stop = true; } condition.notify_all(); - for (auto& worker : workers) { + for (auto& worker : workers) + { worker.join(); } } - template - auto enqueue(F&& f, Args&&... args) -> std::future { + template + auto enqueue(F&& f, Args&&... args) -> std::future + { using return_type = decltype(f(args...)); auto task = std::make_shared>( - std::bind(std::forward(f), std::forward(args)...) - ); + std::bind(std::forward(f), std::forward(args)...)); std::future res = task->get_future(); { std::unique_lock lock(queueMutex); - if (stop) throw std::runtime_error("Enqueue on stopped ThreadPool"); + if (stop) + throw std::runtime_error("Enqueue on stopped ThreadPool"); tasks.emplace([task]() { (*task)(); }); } condition.notify_one(); return res; } -private: + private: std::vector workers; std::queue> tasks; std::mutex queueMutex; @@ -75,27 +84,23 @@ class ThreadPool { bool stop = false; }; - namespace ctrace { -class ToolInvoker { - public: - ToolInvoker(ctrace::ProgramConfig config, - uint8_t nbThreadPool, - std::launch policy, + class ToolInvoker + { + public: + ToolInvoker(ctrace::ProgramConfig config, uint8_t nbThreadPool, std::launch policy, std::shared_ptr output_capture = nullptr) - : m_config(config), - m_nbThreadPool(nbThreadPool), - m_policy(policy), + : m_config(config), m_nbThreadPool(nbThreadPool), m_policy(policy), m_output_capture(output_capture) { std::cout << "\033[36mInitializing ToolInvoker...\033[0m\n"; - tools["cppcheck"] = std::make_unique(); + tools["cppcheck"] = std::make_unique(); tools["flawfinder"] = std::make_unique(); - tools["tscancode"] = std::make_unique(); - tools["ikos"] = std::make_unique(); + tools["tscancode"] = std::make_unique(); + tools["ikos"] = std::make_unique(); tools["ctrace_stack_analyzer"] = std::make_unique(); tools["dyn_tools_1"] = std::make_unique(); tools["dyn_tools_2"] = std::make_unique(); @@ -124,18 +129,19 @@ class ToolInvoker { std::vector> results; ThreadPool pool(m_nbThreadPool); - for (const auto& tool_name : static_tools) { + for (const auto& tool_name : static_tools) + { // tools.at(tool_name)->execute(file, m_config); - results.push_back(std::async( - m_policy, - [this, tool_name, file]() - { - auto& tool = tools.at(tool_name); - ctrace::Thread::Output::CaptureContext ctx{m_output_capture, tool_name, true}; - ctrace::Thread::Output::ScopedCapture capture(m_output_capture ? &ctx : nullptr); - return tool->execute(file, m_config); - } - )); + results.push_back(std::async(m_policy, + [this, tool_name, file]() + { + auto& tool = tools.at(tool_name); + ctrace::Thread::Output::CaptureContext ctx{ + m_output_capture, tool_name, true}; + ctrace::Thread::Output::ScopedCapture capture( + m_output_capture ? &ctx : nullptr); + return tool->execute(file, m_config); + })); } for (auto& result : results) { @@ -156,7 +162,8 @@ class ToolInvoker { } // Execute a specific tool list - void runSpecificTools(const std::vector& tool_names, const std::string& file) const + void runSpecificTools(const std::vector& tool_names, + const std::string& file) const { for (const auto& name : tool_names) { @@ -164,19 +171,21 @@ class ToolInvoker { { auto& tool = tools.at(name); ctrace::Thread::Output::CaptureContext ctx{m_output_capture, name, true}; - ctrace::Thread::Output::ScopedCapture capture(m_output_capture ? &ctx : nullptr); + ctrace::Thread::Output::ScopedCapture capture(m_output_capture ? &ctx + : nullptr); tool->execute(file, m_config); } else { ctrace::Thread::Output::CaptureContext ctx{m_output_capture, name, true}; - ctrace::Thread::Output::ScopedCapture capture(m_output_capture ? &ctx : nullptr); + ctrace::Thread::Output::ScopedCapture capture(m_output_capture ? &ctx + : nullptr); ctrace::Thread::Output::cerr("\033[31mUnknown tool: " + name + "\033[0m"); } } } - private: + private: std::unordered_map> tools; std::vector static_tools; std::vector dynamic_tools; @@ -186,6 +195,6 @@ class ToolInvoker { std::shared_ptr m_ipc; std::shared_ptr m_output_capture; }; -} +} // namespace ctrace #endif // TOOLS_INVOKER_HPP diff --git a/include/Process/UnixProcess.hpp b/include/Process/UnixProcess.hpp index 840a1f3..7975408 100644 --- a/include/Process/UnixProcess.hpp +++ b/include/Process/UnixProcess.hpp @@ -9,24 +9,28 @@ #include #include "Process.hpp" -extern char **environ; +extern char** environ; -class UnixProcessWithPosixSpawn : public Process { -public: +class UnixProcessWithPosixSpawn : public Process +{ + public: UnixProcessWithPosixSpawn(const std::string& command, std::vector args) : command_(command), pid_(0), log_fd_(-1), resolved_path_("") { additional_args_ = args; } - ~UnixProcessWithPosixSpawn() override { - if (log_fd_ != -1) { + ~UnixProcessWithPosixSpawn() override + { + if (log_fd_ != -1) + { close(log_fd_); } } -protected: - void prepare() override { + protected: + void prepare() override + { resolveCommandPath(); checkPermissions(); prepareArguments(); @@ -38,29 +42,36 @@ class UnixProcessWithPosixSpawn : public Process { posix_spawn_file_actions_t file_actions; posix_spawnattr_t attr; - if (posix_spawn_file_actions_init(&file_actions) != 0) { - throw std::runtime_error("Failed to init file actions: " + std::string(strerror(errno))); + if (posix_spawn_file_actions_init(&file_actions) != 0) + { + throw std::runtime_error("Failed to init file actions: " + + std::string(strerror(errno))); } - if (posix_spawnattr_init(&attr) != 0) { + if (posix_spawnattr_init(&attr) != 0) + { throw std::runtime_error("Failed to init spawn attr: " + std::string(strerror(errno))); } if (posix_spawn_file_actions_adddup2(&file_actions, log_fd_, STDOUT_FILENO) != 0 || - posix_spawn_file_actions_adddup2(&file_actions, log_fd_, STDERR_FILENO) != 0) { - throw std::runtime_error("Failed to setup file redirection: " + std::string(strerror(errno))); + posix_spawn_file_actions_adddup2(&file_actions, log_fd_, STDERR_FILENO) != 0) + { + throw std::runtime_error("Failed to setup file redirection: " + + std::string(strerror(errno))); } // Utiliser arguments (de la classe de base) au lieu de m_arguments std::vector argv; - for (auto& arg : m_arguments) { + for (auto& arg : m_arguments) + { argv.push_back(const_cast(arg.c_str())); } argv.push_back(nullptr); - int status = posix_spawn(&pid_, resolved_path_.c_str(), &file_actions, &attr, - argv.data(), environ); + int status = + posix_spawn(&pid_, resolved_path_.c_str(), &file_actions, &attr, argv.data(), environ); - if (status != 0) { + if (status != 0) + { throw std::runtime_error("posix_spawn failed: " + std::string(strerror(status))); } @@ -68,20 +79,24 @@ class UnixProcessWithPosixSpawn : public Process { posix_spawnattr_destroy(&attr); } - void cleanup() override { - if (pid_ > 0) { + void cleanup() override + { + if (pid_ > 0) + { int status; waitpid(pid_, &status, 0); pid_ = 0; } captureLogs(); - if (log_fd_ != -1) { + if (log_fd_ != -1) + { close(log_fd_); log_fd_ = -1; } } - void prepareArguments() override { + void prepareArguments() override + { m_arguments.clear(); // Le premier argument doit être le nom de la commande @@ -89,13 +104,13 @@ class UnixProcessWithPosixSpawn : public Process { m_arguments.push_back(cmd_name); // Ajouter les arguments supplémentaires - m_arguments.insert(m_arguments.end(), - additional_args_.begin(), - additional_args_.end()); + m_arguments.insert(m_arguments.end(), additional_args_.begin(), additional_args_.end()); } - void captureLogs() override { - if (log_fd_ == -1) { + void captureLogs() override + { + if (log_fd_ == -1) + { return; } @@ -105,7 +120,8 @@ class UnixProcessWithPosixSpawn : public Process { char chunk[1024]; ssize_t bytes_read; - while ((bytes_read = read(log_fd_, chunk, sizeof(chunk)-1)) > 0) { + while ((bytes_read = read(log_fd_, chunk, sizeof(chunk) - 1)) > 0) + { chunk[bytes_read] = '\0'; buffer += chunk; } @@ -113,19 +129,25 @@ class UnixProcessWithPosixSpawn : public Process { logOutput = buffer; } -private: - void setupLogging() { + private: + void setupLogging() + { char temp_path[] = "/tmp/process_log_XXXXXX"; log_fd_ = mkstemp(temp_path); - if (log_fd_ == -1) { - throw std::runtime_error("Failed to create temp log file: " + std::string(strerror(errno))); + if (log_fd_ == -1) + { + throw std::runtime_error("Failed to create temp log file: " + + std::string(strerror(errno))); } unlink(temp_path); } - void resolveCommandPath() { - if (command_.find('/') != std::string::npos) { - if (access(command_.c_str(), F_OK) == 0) { + void resolveCommandPath() + { + if (command_.find('/') != std::string::npos) + { + if (access(command_.c_str(), F_OK) == 0) + { resolved_path_ = command_; return; } @@ -133,7 +155,8 @@ class UnixProcessWithPosixSpawn : public Process { } char* path_env = getenv("PATH"); - if (!path_env) { + if (!path_env) + { throw std::runtime_error("PATH environment variable not set"); } @@ -141,11 +164,13 @@ class UnixProcessWithPosixSpawn : public Process { std::string::size_type start = 0; std::string::size_type end; - while ((end = path.find(':', start)) != std::string::npos) { + while ((end = path.find(':', start)) != std::string::npos) + { std::string dir = path.substr(start, end - start); std::string full_path = dir + "/" + command_; - if (access(full_path.c_str(), F_OK) == 0) { + if (access(full_path.c_str(), F_OK) == 0) + { resolved_path_ = full_path; return; } @@ -154,7 +179,8 @@ class UnixProcessWithPosixSpawn : public Process { std::string last_dir = path.substr(start); std::string last_path = last_dir + "/" + command_; - if (access(last_path.c_str(), F_OK) == 0) { + if (access(last_path.c_str(), F_OK) == 0) + { resolved_path_ = last_path; return; } @@ -162,17 +188,23 @@ class UnixProcessWithPosixSpawn : public Process { throw std::runtime_error("Command not found in PATH: " + command_); } - void checkPermissions() { - if (access(resolved_path_.c_str(), X_OK) != 0) { - throw std::runtime_error("Command not executable: " + resolved_path_ + ": " + std::string(strerror(errno))); + void checkPermissions() + { + if (access(resolved_path_.c_str(), X_OK) != 0) + { + throw std::runtime_error("Command not executable: " + resolved_path_ + ": " + + std::string(strerror(errno))); } struct stat st; - if (stat(resolved_path_.c_str(), &st) != 0) { - throw std::runtime_error("Cannot stat command: " + resolved_path_ + ": " + std::string(strerror(errno))); + if (stat(resolved_path_.c_str(), &st) != 0) + { + throw std::runtime_error("Cannot stat command: " + resolved_path_ + ": " + + std::string(strerror(errno))); } - if (!S_ISREG(st.st_mode)) { + if (!S_ISREG(st.st_mode)) + { throw std::runtime_error("Command is not a regular file: " + resolved_path_); } } diff --git a/include/Process/WinProcess.hpp b/include/Process/WinProcess.hpp index fab930d..0adfc71 100644 --- a/include/Process/WinProcess.hpp +++ b/include/Process/WinProcess.hpp @@ -6,33 +6,38 @@ #include #include -class WindowsProcess : public Process { -public: +class WindowsProcess : public Process +{ + public: WindowsProcess(const std::string& command) : command(command) {} -protected: - void prepare() override { + protected: + void prepare() override + { std::cout << "Preparing Windows process\n"; prepareArguments(); } - void run() override { + void run() override + { std::cout << "Running Windows process\n"; // Préparer la commande avec les arguments std::string cmdLine = command; - for (const auto& arg : arguments) { + for (const auto& arg : arguments) + { cmdLine += " " + arg; } - STARTUPINFO si = { sizeof(si) }; + STARTUPINFO si = {sizeof(si)}; PROCESS_INFORMATION pi; - SECURITY_ATTRIBUTES sa = { sizeof(sa), NULL, TRUE }; + SECURITY_ATTRIBUTES sa = {sizeof(sa), NULL, TRUE}; // Créer un fichier pour capturer les logs HANDLE logFile = CreateFile("process.log", GENERIC_WRITE, FILE_SHARE_READ, &sa, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); - if (logFile == INVALID_HANDLE_VALUE) { + if (logFile == INVALID_HANDLE_VALUE) + { throw std::runtime_error("Failed to create log file"); } @@ -41,8 +46,9 @@ class WindowsProcess : public Process { si.dwFlags |= STARTF_USESTDHANDLES; // Lancer le processus - if (!CreateProcess(NULL, const_cast(cmdLine.c_str()), NULL, NULL, TRUE, 0, - NULL, NULL, &si, &pi)) { + if (!CreateProcess(NULL, const_cast(cmdLine.c_str()), NULL, NULL, TRUE, 0, NULL, + NULL, &si, &pi)) + { CloseHandle(logFile); throw std::runtime_error("Failed to create process"); } @@ -56,28 +62,33 @@ class WindowsProcess : public Process { captureLogs(); } - void cleanup() override { + void cleanup() override + { std::cout << "Cleaning up Windows process\n"; DeleteFile("process.log"); // Supprimer le fichier de logs temporaire } - void prepareArguments() override { + void prepareArguments() override + { std::cout << "Preparing arguments for Windows\n"; arguments.clear(); - arguments.push_back("/C"); // Exemple pour `cmd.exe` + arguments.push_back("/C"); // Exemple pour `cmd.exe` arguments.push_back("dir"); // Commande `dir` pour lister les fichiers } - void captureLogs() override { + void captureLogs() override + { std::cout << "Capturing logs for Windows\n"; FILE* logFile = fopen("process.log", "r"); - if (!logFile) { + if (!logFile) + { throw std::runtime_error("Failed to open log file"); } char buffer[1024]; logOutput.clear(); - while (fgets(buffer, sizeof(buffer), logFile)) { + while (fgets(buffer, sizeof(buffer), logFile)) + { logOutput += buffer; } fclose(logFile); @@ -85,7 +96,7 @@ class WindowsProcess : public Process { std::cout << "Logs captured: " << logOutput << "\n"; } -private: + private: std::string command; }; diff --git a/include/attributes.hpp b/include/attributes.hpp index 5d2c75a..f9be4fd 100644 --- a/include/attributes.hpp +++ b/include/attributes.hpp @@ -2,19 +2,19 @@ #define COMPILERLIB_ATTRIBUTES_HPP #if defined(__cplusplus) -# if defined(__has_cpp_attribute) -# if __has_cpp_attribute(nodiscard) -# define CT_NODISCARD [[nodiscard]] -# endif -# endif +#if defined(__has_cpp_attribute) +#if __has_cpp_attribute(nodiscard) +#define CT_NODISCARD [[nodiscard]] +#endif +#endif #endif #ifndef CT_NODISCARD -# if defined(__GNUC__) || defined(__clang__) -# define CT_NODISCARD __attribute__((warn_unused_result)) -# else -# define CT_NODISCARD -# endif +#if defined(__GNUC__) || defined(__clang__) +#define CT_NODISCARD __attribute__((warn_unused_result)) +#else +#define CT_NODISCARD +#endif #endif #endif // COMPILERLIB_ATTRIBUTES_HPP diff --git a/include/ctrace_defs/types.hpp b/include/ctrace_defs/types.hpp index 38d59de..46cc9c6 100644 --- a/include/ctrace_defs/types.hpp +++ b/include/ctrace_defs/types.hpp @@ -14,17 +14,16 @@ namespace ctrace_defs */ enum class LanguageType : int { - C = 0, + C = 0, CPP = 1, }; inline const std::vector IPC_TYPES = { - "standardIO", // default - "socket", - "serve", - // "pipe" // future implementation - }; + "standardIO", // default + "socket", "serve", + // "pipe" // future implementation + }; -} +} // namespace ctrace_defs #endif // TYPES_HPP diff --git a/include/ctrace_tools/colors.hpp b/include/ctrace_tools/colors.hpp index 6fac869..823ce2f 100644 --- a/include/ctrace_tools/colors.hpp +++ b/include/ctrace_tools/colors.hpp @@ -14,14 +14,14 @@ namespace ctrace * and a reset code to restore the default terminal color. These can * be used to format text output in the terminal with colors. */ - const std::string RESET = "\033[0m"; - const std::string RED = "\033[31m"; - const std::string GREEN = "\033[32m"; - const std::string YELLOW = "\033[33m"; - const std::string BLUE = "\033[34m"; - const std::string MAGENTA = "\033[35m"; - const std::string CYAN = "\033[36m"; - } -} + const std::string RESET = "\033[0m"; + const std::string RED = "\033[31m"; + const std::string GREEN = "\033[32m"; + const std::string YELLOW = "\033[33m"; + const std::string BLUE = "\033[34m"; + const std::string MAGENTA = "\033[35m"; + const std::string CYAN = "\033[36m"; + } // namespace Color +} // namespace ctrace #endif // COLORS_HPP diff --git a/include/ctrace_tools/languageType.hpp b/include/ctrace_tools/languageType.hpp index eb30799..d30a299 100644 --- a/include/ctrace_tools/languageType.hpp +++ b/include/ctrace_tools/languageType.hpp @@ -24,6 +24,6 @@ namespace ctrace_tools * not throw exceptions. */ [[nodiscard]] ctrace_defs::LanguageType detectLanguage(std::string_view filename) noexcept; -} +} // namespace ctrace_tools #endif // LANGUAGE_TYPE_HPP diff --git a/include/ctrace_tools/mangle.hpp b/include/ctrace_tools/mangle.hpp index 1c987e5..012518a 100644 --- a/include/ctrace_tools/mangle.hpp +++ b/include/ctrace_tools/mangle.hpp @@ -13,8 +13,7 @@ namespace ctrace_tools::mangle * The `StringLike` concept ensures that any type passed to functions * requiring it can be implicitly converted to `std::string_view`. */ - template - concept StringLike = std::convertible_to; + template concept StringLike = std::convertible_to; // TODO: add mangling for windows /** @@ -35,7 +34,7 @@ namespace ctrace_tools::mangle * should not be ignored. It is also `noexcept`, indicating that it * does not throw exceptions. */ - [[nodiscard]]bool isMangled(StringLike auto name) noexcept + [[nodiscard]] bool isMangled(StringLike auto name) noexcept { int status = 0; std::string_view sv{name}; @@ -45,10 +44,8 @@ namespace ctrace_tools::mangle return false; } - std::unique_ptr demangled( - abi::__cxa_demangle(sv.data(), nullptr, nullptr, &status), - std::free - ); + std::unique_ptr demangled( + abi::__cxa_demangle(sv.data(), nullptr, nullptr, &status), std::free); return status == 0; } @@ -65,9 +62,7 @@ namespace ctrace_tools::mangle * * @note The implementation of this function is not provided in the current file. */ - [[nodiscard]] std::string mangleFunction( - const std::string& namespaceName, - const std::string& functionName, - const std::vector& paramTypes - ); -}; + [[nodiscard]] std::string mangleFunction(const std::string& namespaceName, + const std::string& functionName, + const std::vector& paramTypes); +}; // namespace ctrace_tools::mangle diff --git a/include/ctrace_tools/strings.hpp b/include/ctrace_tools/strings.hpp index 30ad10a..f334393 100644 --- a/include/ctrace_tools/strings.hpp +++ b/include/ctrace_tools/strings.hpp @@ -8,9 +8,9 @@ namespace ctrace_tools { -namespace strings -{ - /** + namespace strings + { + /** * @brief Splits a string into parts based on commas. * * This function takes an input string and splits it into a vector of substrings, @@ -26,9 +26,9 @@ namespace strings * should not be ignored. It is also `noexcept`, indicating that it * does not throw exceptions. */ - [[nodiscard]] std::vector splitByComma(std::string_view input) noexcept; -} + [[nodiscard]] std::vector splitByComma(std::string_view input) noexcept; + } // namespace strings -} +} // namespace ctrace_tools #endif // STRINGS_HPP diff --git a/main.cpp b/main.cpp index 143cc30..5e8b1c6 100644 --- a/main.cpp +++ b/main.cpp @@ -4,12 +4,11 @@ #include -int main(int argc, char *argv[]) +int main(int argc, char* argv[]) { ctrace::ProgramConfig config = ctrace::buildConfig(argc, argv); - std::cout << ctrace::Color::GREEN - << "CoreTrace - Comprehensive Tracing and Analysis Tool" + std::cout << ctrace::Color::GREEN << "CoreTrace - Comprehensive Tracing and Analysis Tool" << ctrace::Color::RESET << std::endl; if (config.global.ipc == "serve") diff --git a/src/App/Config.cpp b/src/App/Config.cpp index 4935a68..7ab7158 100644 --- a/src/App/Config.cpp +++ b/src/App/Config.cpp @@ -8,7 +8,7 @@ namespace ctrace { - CT_NODISCARD ProgramConfig buildConfig(int argc, char *argv[]) + CT_NODISCARD ProgramConfig buildConfig(int argc, char* argv[]) { auto parser = createArgumentParser(); ArgumentManager argManager(std::move(parser)); @@ -46,16 +46,15 @@ namespace ctrace processor.execute("--help", ""); std::exit(0); } - if (!(argManager.getOptionValue("--ipc") == "serve") - && (argManager.hasOption("--serve-host") - || argManager.hasOption("--serve-port")) - ) + if (!(argManager.getOptionValue("--ipc") == "serve") && + (argManager.hasOption("--serve-host") || argManager.hasOption("--serve-port"))) { - std::cout << "[INFO] UNCONSISTENT SERVER OPTIONS: --serve-host or --serve-port needed --ipc=server." + std::cout << "[INFO] UNCONSISTENT SERVER OPTIONS: --serve-host or --serve-port needed " + "--ipc=server." << std::endl; std::exit(1); } return config; } -} +} // namespace ctrace diff --git a/src/App/Files.cpp b/src/App/Files.cpp index a052914..f67b586 100644 --- a/src/App/Files.cpp +++ b/src/App/Files.cpp @@ -47,7 +47,8 @@ namespace ctrace const auto manifest = json::parse(buffer.str(), nullptr, false); if (!manifest.is_discarded()) { - const std::filesystem::path manifestDir = std::filesystem::path(entry).parent_path(); + const std::filesystem::path manifestDir = + std::filesystem::path(entry).parent_path(); const auto appendFromArray = [&](const json& arr) -> bool { @@ -56,23 +57,28 @@ namespace ctrace { if (item.is_string()) { - appended |= appendResolved(item.get(), manifestDir); + appended |= + appendResolved(item.get(), manifestDir); } else if (item.is_object()) { - if (const auto it = item.find("file"); it != item.end() && it->is_string()) + if (const auto it = item.find("file"); + it != item.end() && it->is_string()) { - appended |= appendResolved(it->get(), manifestDir); + appended |= + appendResolved(it->get(), manifestDir); } - else if (const auto itSrc = item.find("src_file"); itSrc != item.end() - && itSrc->is_string()) + else if (const auto itSrc = item.find("src_file"); + itSrc != item.end() && itSrc->is_string()) { - appended |= appendResolved(itSrc->get(), manifestDir); + appended |= + appendResolved(itSrc->get(), manifestDir); } - else if (const auto itPath = item.find("path"); itPath != item.end() - && itPath->is_string()) + else if (const auto itPath = item.find("path"); + itPath != item.end() && itPath->is_string()) { - appended |= appendResolved(itPath->get(), manifestDir); + appended |= + appendResolved(itPath->get(), manifestDir); } } } @@ -85,31 +91,33 @@ namespace ctrace } else if (manifest.is_object()) { - if (const auto it = manifest.find("files"); it != manifest.end() && it->is_array()) + if (const auto it = manifest.find("files"); + it != manifest.end() && it->is_array()) { expanded = appendFromArray(*it); } - else if (const auto it = manifest.find("sources"); it != manifest.end() && it->is_array()) + else if (const auto it = manifest.find("sources"); + it != manifest.end() && it->is_array()) { expanded = appendFromArray(*it); } - else if (const auto it = manifest.find("compile_commands"); it != manifest.end() - && it->is_array()) + else if (const auto it = manifest.find("compile_commands"); + it != manifest.end() && it->is_array()) { expanded = appendFromArray(*it); } - else if (const auto itFile = manifest.find("file"); itFile != manifest.end() - && itFile->is_string()) + else if (const auto itFile = manifest.find("file"); + itFile != manifest.end() && itFile->is_string()) { expanded = appendResolved(itFile->get(), manifestDir); } - else if (const auto itSrc = manifest.find("src_file"); itSrc != manifest.end() - && itSrc->is_string()) + else if (const auto itSrc = manifest.find("src_file"); + itSrc != manifest.end() && itSrc->is_string()) { expanded = appendResolved(itSrc->get(), manifestDir); } - else if (const auto itPath = manifest.find("path"); itPath != manifest.end() - && itPath->is_string()) + else if (const auto itPath = manifest.find("path"); + itPath != manifest.end() && itPath->is_string()) { expanded = appendResolved(itPath->get(), manifestDir); } @@ -126,4 +134,4 @@ namespace ctrace return sourceFiles; } -} +} // namespace ctrace diff --git a/src/App/Runner.cpp b/src/App/Runner.cpp index 93bf0c9..b0c4f0f 100644 --- a/src/App/Runner.cpp +++ b/src/App/Runner.cpp @@ -13,8 +13,7 @@ namespace ctrace { CT_NODISCARD int run_server(const ProgramConfig& config) { - std::cout << "\033[36mStarting IPC server at " - << config.global.serverHost << ":" + std::cout << "\033[36mStarting IPC server at " << config.global.serverHost << ":" << config.global.serverPort << "...\033[0m\n"; ConsoleLogger logger; ApiHandler apiHandler(logger); @@ -25,10 +24,12 @@ namespace ctrace CT_NODISCARD int run_cli_analysis(const ProgramConfig& config) { - ctrace::ToolInvoker invoker(config, std::thread::hardware_concurrency(), config.global.hasAsync); + ctrace::ToolInvoker invoker(config, std::thread::hardware_concurrency(), + config.global.hasAsync); std::cout << ctrace::Color::CYAN << "asynchronous execution: " - << (config.global.hasAsync == std::launch::async ? ctrace::Color::GREEN : ctrace::Color::RED) + << (config.global.hasAsync == std::launch::async ? ctrace::Color::GREEN + : ctrace::Color::RED) << (config.global.hasAsync == std::launch::async ? "enabled" : "disabled") << ctrace::Color::RESET << std::endl; @@ -44,37 +45,38 @@ namespace ctrace << (config.global.hasDynamicAnalysis ? ctrace::Color::GREEN : ctrace::Color::RED) << config.global.hasDynamicAnalysis << ctrace::Color::RESET << std::endl; - std::cout << ctrace::Color::CYAN << "Report file: " - << ctrace::Color::YELLOW << config.global.report_file - << ctrace::Color::RESET << std::endl; + std::cout << ctrace::Color::CYAN << "Report file: " << ctrace::Color::YELLOW + << config.global.report_file << ctrace::Color::RESET << std::endl; - std::cout << ctrace::Color::CYAN << "entry point: " - << ctrace::Color::YELLOW << config.global.entry_points - << ctrace::Color::RESET << std::endl; + std::cout << ctrace::Color::CYAN << "entry point: " << ctrace::Color::YELLOW + << config.global.entry_points << ctrace::Color::RESET << std::endl; std::vector sourceFiles = ctrace::resolveSourceFiles(config); for (const auto& file : sourceFiles) { - std::cout << ctrace::Color::CYAN << "File: " - << ctrace::Color::YELLOW << file << ctrace::Color::RESET << std::endl; + std::cout << ctrace::Color::CYAN << "File: " << ctrace::Color::YELLOW << file + << ctrace::Color::RESET << std::endl; if (config.global.hasStaticAnalysis) { - std::cout << ctrace::Color::CYAN << "Running static analysis..." << ctrace::Color::RESET << std::endl; + std::cout << ctrace::Color::CYAN << "Running static analysis..." + << ctrace::Color::RESET << std::endl; invoker.runStaticTools(file); } if (config.global.hasDynamicAnalysis) { - std::cout << ctrace::Color::CYAN << "Running dynamic analysis..." << ctrace::Color::RESET << std::endl; + std::cout << ctrace::Color::CYAN << "Running dynamic analysis..." + << ctrace::Color::RESET << std::endl; invoker.runDynamicTools(file); } if (config.global.hasInvokedSpecificTools) { - std::cout << ctrace::Color::CYAN << "Running specific tools..." << ctrace::Color::RESET << std::endl; + std::cout << ctrace::Color::CYAN << "Running specific tools..." + << ctrace::Color::RESET << std::endl; invoker.runSpecificTools(config.global.specificTools, file); } } return 0; } -} +} // namespace ctrace diff --git a/src/ArgumentParser/ArgumentManager.cpp b/src/ArgumentParser/ArgumentManager.cpp index 874a102..8f51bed 100644 --- a/src/ArgumentParser/ArgumentManager.cpp +++ b/src/ArgumentParser/ArgumentManager.cpp @@ -2,17 +2,22 @@ #include ArgumentManager::ArgumentManager(std::unique_ptr parser) - : _parser(std::move(parser)) {} + : _parser(std::move(parser)) +{ +} -void ArgumentManager::addOption(const std::string& name, bool hasArgument, char shortName) { +void ArgumentManager::addOption(const std::string& name, bool hasArgument, char shortName) +{ _parser->addOption(name, hasArgument, shortName); } -void ArgumentManager::addFlag(const std::string& name, char shortName) { +void ArgumentManager::addFlag(const std::string& name, char shortName) +{ _parser->addFlag(name, shortName); } -void ArgumentManager::parse(int argc, char* argv[]) { +void ArgumentManager::parse(int argc, char* argv[]) +{ if (!_parser->parse(argc, argv)) { std::cerr << "Error: " << _parser->getErrorMessage() << std::endl; @@ -24,7 +29,8 @@ void ArgumentManager::parse(int argc, char* argv[]) { } } -bool ArgumentManager::hasOption(const std::string& name) const { +bool ArgumentManager::hasOption(const std::string& name) const +{ return _parser->hasOption(name); } @@ -33,6 +39,7 @@ bool ArgumentManager::hasNoOption() const return _hasNoOption; } -std::string ArgumentManager::getOptionValue(const std::string& name) const { +std::string ArgumentManager::getOptionValue(const std::string& name) const +{ return _parser->getOptionValue(name); } diff --git a/src/ArgumentParser/ArgumentParserFactory.cpp b/src/ArgumentParser/ArgumentParserFactory.cpp index 081d49d..a22e3b7 100644 --- a/src/ArgumentParser/ArgumentParserFactory.cpp +++ b/src/ArgumentParser/ArgumentParserFactory.cpp @@ -2,7 +2,8 @@ #include "ArgumentParser/CLI11/CLI11ArgumentParser.hpp" #include "ArgumentParser/GetOpt/GetOptArgumentParser.hpp" -std::unique_ptr createArgumentParser() { +std::unique_ptr createArgumentParser() +{ #ifdef USE_GETOPT return std::make_unique(); #else diff --git a/src/ArgumentParser/BaseArgumentParser.cpp b/src/ArgumentParser/BaseArgumentParser.cpp index 0713f3c..dbc7ea9 100644 --- a/src/ArgumentParser/BaseArgumentParser.cpp +++ b/src/ArgumentParser/BaseArgumentParser.cpp @@ -3,15 +3,18 @@ BaseArgumentParser::BaseArgumentParser() : _lastError(ErrorCode::SUCCESS), _errorMessage("") {} -void BaseArgumentParser::setError(ErrorCode code, const std::string& message) { +void BaseArgumentParser::setError(ErrorCode code, const std::string& message) +{ _lastError = code; _errorMessage = message; } -ErrorCode BaseArgumentParser::getLastError() const { +ErrorCode BaseArgumentParser::getLastError() const +{ return _lastError; } -std::string BaseArgumentParser::getErrorMessage() const { +std::string BaseArgumentParser::getErrorMessage() const +{ return _errorMessage; } diff --git a/src/ArgumentParser/CLI11/CLI11ArgumentParser.cpp b/src/ArgumentParser/CLI11/CLI11ArgumentParser.cpp index f93fbc7..c4d179f 100644 --- a/src/ArgumentParser/CLI11/CLI11ArgumentParser.cpp +++ b/src/ArgumentParser/CLI11/CLI11ArgumentParser.cpp @@ -3,39 +3,47 @@ #include "../../../external/CLI11.hpp" #include -class CLI11ArgumentParser : public BaseArgumentParser { -private: +class CLI11ArgumentParser : public BaseArgumentParser +{ + private: CLI::App app{"CLI11 Argument Parser"}; std::vector _options; std::vector> _flags; std::vector> _values; - bool optionExists(const std::string& name, char shortName) const { - for (const auto& opt : _options) { - if (opt.name == name || (shortName != '\0' && opt.shortName == shortName)) { + bool optionExists(const std::string& name, char shortName) const + { + for (const auto& opt : _options) + { + if (opt.name == name || (shortName != '\0' && opt.shortName == shortName)) + { return true; } } return false; } - static std::string stripDashes(const std::string& name) { + static std::string stripDashes(const std::string& name) + { std::string stripped = name; - while (!stripped.empty() && stripped[0] == '-') { + while (!stripped.empty() && stripped[0] == '-') + { stripped.erase(0, 1); } return stripped; } -public: + public: CLI11ArgumentParser() { // std::cout << "CLI11 is called" << std::endl; app.set_help_flag(""); } - void addOption(const std::string& name, bool hasArgument, char shortName) override { - if (optionExists(name, shortName)) { + void addOption(const std::string& name, bool hasArgument, char shortName) override + { + if (optionExists(name, shortName)) + { setError(ErrorCode::INVALID_OPTION, "Option already exists: " + name); return; } @@ -49,18 +57,24 @@ class CLI11ArgumentParser : public BaseArgumentParser { std::string longOpt = stripDashes(name); std::string shortOpt = shortName != '\0' ? std::string(1, shortName) : ""; - if (hasArgument) { + if (hasArgument) + { auto* value = new std::string(); _values.emplace_back(longOpt, value); - auto* cliOption = app.add_option("-" + shortOpt + "," + name, *value, "Option with argument"); - if (!cliOption) { + auto* cliOption = + app.add_option("-" + shortOpt + "," + name, *value, "Option with argument"); + if (!cliOption) + { setError(ErrorCode::INVALID_OPTION, "Failed to add option: " + name); } - } else { + } + else + { auto* flag = new bool(false); _flags.emplace_back(longOpt, flag); auto* cliFlag = app.add_flag("-" + shortOpt + "," + name, *flag, "Flag option"); - if (!cliFlag) { + if (!cliFlag) + { setError(ErrorCode::INVALID_OPTION, "Failed to add flag: " + name); } } @@ -68,35 +82,49 @@ class CLI11ArgumentParser : public BaseArgumentParser { _options.push_back(opt); } - void addFlag(const std::string& name, char shortName) override { + void addFlag(const std::string& name, char shortName) override + { addOption(name, false, shortName); } - bool parse(int argc, char* argv[]) override { + bool parse(int argc, char* argv[]) override + { setError(ErrorCode::SUCCESS); - try { + try + { app.parse(argc, argv); - } catch (const CLI::ParseError& e) { - if (e.get_exit_code() != 0) { + } + catch (const CLI::ParseError& e) + { + if (e.get_exit_code() != 0) + { setError(ErrorCode::INVALID_OPTION, e.what()); return false; } } - for (auto& opt : _options) { + for (auto& opt : _options) + { std::string longOpt = stripDashes(opt.name); - if (opt.hasArgument) { - for (const auto& [name, value] : _values) { - if (name == longOpt && !value->empty()) { + if (opt.hasArgument) + { + for (const auto& [name, value] : _values) + { + if (name == longOpt && !value->empty()) + { opt.isSet = true; opt.value = *value; break; } } - } else { - for (const auto& [name, flag] : _flags) { - if (name == longOpt && *flag) { + } + else + { + for (const auto& [name, flag] : _flags) + { + if (name == longOpt && *flag) + { opt.isSet = true; break; } @@ -107,29 +135,40 @@ class CLI11ArgumentParser : public BaseArgumentParser { return true; } - bool hasOption(const std::string& name) const override { - for (const auto& opt : _options) { - if (opt.name == name || (opt.shortName != '\0' && std::string(1, opt.shortName) == name)) { + bool hasOption(const std::string& name) const override + { + for (const auto& opt : _options) + { + if (opt.name == name || + (opt.shortName != '\0' && std::string(1, opt.shortName) == name)) + { return opt.isSet; } } return false; } - std::string getOptionValue(const std::string& name) const override { - for (const auto& opt : _options) { - if (opt.name == name || (opt.shortName != '\0' && std::string(1, opt.shortName) == name)) { + std::string getOptionValue(const std::string& name) const override + { + for (const auto& opt : _options) + { + if (opt.name == name || + (opt.shortName != '\0' && std::string(1, opt.shortName) == name)) + { return opt.value; } } return ""; } - ~CLI11ArgumentParser() override { - for (auto& [_, flag] : _flags) { + ~CLI11ArgumentParser() override + { + for (auto& [_, flag] : _flags) + { delete flag; } - for (auto& [_, value] : _values) { + for (auto& [_, value] : _values) + { delete value; } } diff --git a/src/ArgumentParser/GetOpt/GetoptArgumentParser.cpp b/src/ArgumentParser/GetOpt/GetoptArgumentParser.cpp index e6c787c..eb221fc 100644 --- a/src/ArgumentParser/GetOpt/GetoptArgumentParser.cpp +++ b/src/ArgumentParser/GetOpt/GetoptArgumentParser.cpp @@ -7,22 +7,25 @@ #include "ArgumentParser/BaseArgumentParser.hpp" // Implémentation avec getopt -class GetoptArgumentParser : public BaseArgumentParser { -private: +class GetoptArgumentParser : public BaseArgumentParser +{ + private: std::vector _longOptions; // Pour getopt_long - std::vector _options; // Liste des options enregistrées - std::string _optString; // Chaîne pour getopt (ex: "vha:") + std::vector _options; // Liste des options enregistrées + std::string _optString; // Chaîne pour getopt (ex: "vha:") // Convertit le nom long en format attendu par getopt_long (sans "--") - static std::string stripDashes(const std::string& name) { + static std::string stripDashes(const std::string& name) + { std::string stripped = name; - while (!stripped.empty() && stripped[0] == '-') { + while (!stripped.empty() && stripped[0] == '-') + { stripped.erase(0, 1); } return stripped; } -public: + public: void addOption(const std::string& name, bool hasArgument, char shortName) override { std::cout << "GetOpt is called" << std::endl; @@ -73,30 +76,44 @@ class GetoptArgumentParser : public BaseArgumentParser { int longindex = 0; int c; - while ((c = getopt_long(argc, argv, _optString.c_str(), _longOptions.data(), &longindex)) != -1) { - if (c == '?') { + while ((c = getopt_long(argc, argv, _optString.c_str(), _longOptions.data(), &longindex)) != + -1) + { + if (c == '?') + { setError(ErrorCode::INVALID_OPTION, "Unknown option"); return false; } - if (c == ':') { + if (c == ':') + { setError(ErrorCode::MISSING_ARGUMENT, "Missing argument"); return false; } // Trouver l'option correspondante - for (auto& opt : _options) { - if (c == opt.shortName || (c != 0 && stripDashes(opt.name) == _longOptions[longindex].name)) { + for (auto& opt : _options) + { + if (c == opt.shortName || + (c != 0 && stripDashes(opt.name) == _longOptions[longindex].name)) + { opt.isSet = true; // Vérifier si un argument est attendu - if (opt.hasArgument) { - if (optarg) { + if (opt.hasArgument) + { + if (optarg) + { opt.value = optarg; // Format `--option=value` - } else if (optind < argc && argv[optind][0] != '-') { + } + else if (optind < argc && argv[optind][0] != '-') + { opt.value = argv[optind]; // Format `--option value` - optind++; // Avancer l'index - } else { - setError(ErrorCode::MISSING_ARGUMENT, "Missing argument for option: " + opt.name); + optind++; // Avancer l'index + } + else + { + setError(ErrorCode::MISSING_ARGUMENT, + "Missing argument for option: " + opt.name); return false; } } @@ -108,19 +125,27 @@ class GetoptArgumentParser : public BaseArgumentParser { return true; } - bool hasOption(const std::string& name) const override { - for (const auto& opt : _options) { + bool hasOption(const std::string& name) const override + { + for (const auto& opt : _options) + { std::cout << opt.name << " -> " << opt.shortName << std::endl; - if (opt.name == name || (opt.shortName != '\0' && std::string(1, opt.shortName) == name)) { + if (opt.name == name || + (opt.shortName != '\0' && std::string(1, opt.shortName) == name)) + { return opt.isSet; } } return false; } - std::string getOptionValue(const std::string& name) const override { - for (const auto& opt : _options) { - if (opt.name == name || (opt.shortName != '\0' && std::string(1, opt.shortName) == name)) { + std::string getOptionValue(const std::string& name) const override + { + for (const auto& opt : _options) + { + if (opt.name == name || + (opt.shortName != '\0' && std::string(1, opt.shortName) == name)) + { return opt.value; } } diff --git a/src/Process/Tools/StackAnalyzerToolImplementation.cpp b/src/Process/Tools/StackAnalyzerToolImplementation.cpp index 1fbaa24..c1e99bd 100644 --- a/src/Process/Tools/StackAnalyzerToolImplementation.cpp +++ b/src/Process/Tools/StackAnalyzerToolImplementation.cpp @@ -2,42 +2,47 @@ namespace ctrace { -void StackAnalyzerToolImplementation::execute(const std::string& file, ctrace::ProgramConfig config) const -{ - ctrace::Thread::Output::cout("Running CoreTrace Stack Usage Analyzer on " + file); - std::string filename = file; + void StackAnalyzerToolImplementation::execute(const std::string& file, + ctrace::ProgramConfig config) const + { + ctrace::Thread::Output::cout("Running CoreTrace Stack Usage Analyzer on " + file); + std::string filename = file; - llvm::LLVMContext ctx; - llvm::SMDiagnostic diag; + llvm::LLVMContext ctx; + llvm::SMDiagnostic diag; - ctrace::stack::AnalysisConfig cfg; - cfg.mode = ctrace::stack::AnalysisMode::IR; - cfg.stackLimit = 8 * 1024 * 1024; + ctrace::stack::AnalysisConfig cfg; + cfg.mode = ctrace::stack::AnalysisMode::IR; + cfg.stackLimit = 8 * 1024 * 1024; - auto res = ctrace::stack::analyzeFile(filename, cfg, ctx, diag); + auto res = ctrace::stack::analyzeFile(filename, cfg, ctx, diag); - if (config.global.ipc == "standardIO") - { - if (config.global.hasSarifFormat) + if (config.global.ipc == "standardIO") { - ctrace::Thread::Output::tool_out(ctrace::stack::toJson(res, filename)); - return; - } - if (res.functions.empty()) { + if (config.global.hasSarifFormat) + { + ctrace::Thread::Output::tool_out(ctrace::stack::toJson(res, filename)); + return; + } + if (res.functions.empty()) + { // std::err.print("stack_usage_analyzer", llvm::errs()); return; } llvm::outs() << "Mode: " - << (res.config.mode == ctrace::stack::AnalysisMode::IR ? "IR" : "ABI") - << "\n\n"; + << (res.config.mode == ctrace::stack::AnalysisMode::IR ? "IR" : "ABI") + << "\n\n"; - for (const auto &f : res.functions) { + for (const auto& f : res.functions) + { std::vector param_types; // param_types.reserve(issue.inst->getFunction()->arg_size()); - param_types.push_back("void"); // dummy to avoid empty vector issue // refaire avec les paramèters réels + param_types.push_back( + "void"); // dummy to avoid empty vector issue // refaire avec les paramèters réels // llvm::outs() << "Function: " << f.name << " " << ((ctrace::stack::isMangled(f.name)) ? ctrace::stack::demangle(f.name.c_str()) : "") << "\n"; - llvm::outs() << "Function: " << f.name << " " << "\n"; + llvm::outs() << "Function: " << f.name << " " + << "\n"; llvm::outs() << " local stack: " << f.localStack << " bytes\n"; llvm::outs() << " max stack (including callees): " << f.maxStack << " bytes\n"; @@ -46,31 +51,32 @@ void StackAnalyzerToolImplementation::execute(const std::string& file, ctrace::P if (f.hasInfiniteSelfRecursion) { - llvm::outs() << " [!!!] unconditional self recursion detected (no base case)\n"; + llvm::outs() + << " [!!!] unconditional self recursion detected (no base case)\n"; llvm::outs() << " this will eventually overflow the stack at runtime\n"; } if (f.exceedsLimit) { llvm::outs() << " [!] potential stack overflow: exceeds limit of " - << res.config.stackLimit << " bytes\n"; + << res.config.stackLimit << " bytes\n"; } if (!res.config.quiet) { - for (const auto &d : res.diagnostics) + for (const auto& d : res.diagnostics) { if (d.funcName != f.name) continue; - if (res.config.warningsOnly - && d.severity == ctrace::stack::DiagnosticSeverity::Info) + if (res.config.warningsOnly && + d.severity == ctrace::stack::DiagnosticSeverity::Info) continue; if (d.line != 0) { - llvm::outs() << " at line " << d.line - << ", column " << d.column << "\n"; + llvm::outs() + << " at line " << d.line << ", column " << d.column << "\n"; } llvm::outs() << d.message << "\n"; } @@ -78,16 +84,16 @@ void StackAnalyzerToolImplementation::execute(const std::string& file, ctrace::P llvm::outs() << "\n"; } + } + if (config.global.ipc == "socket") + { + ipc->write(ctrace::stack::toJson(res, filename)); + } } - if (config.global.ipc == "socket") + + std::string StackAnalyzerToolImplementation::name() const { - ipc->write(ctrace::stack::toJson(res, filename)); + return "ctrace_stack_analyzer"; } -} - -std::string StackAnalyzerToolImplementation::name() const -{ - return "ctrace_stack_analyzer"; -} -} +} // namespace ctrace diff --git a/src/Process/Tools/TscancodeToolImplementation.cpp b/src/Process/Tools/TscancodeToolImplementation.cpp index 9b830e5..92900f8 100644 --- a/src/Process/Tools/TscancodeToolImplementation.cpp +++ b/src/Process/Tools/TscancodeToolImplementation.cpp @@ -6,138 +6,139 @@ namespace ctrace { -void TscancodeToolImplementation::execute(const std::string& file, ProgramConfig config) const -{ - ctrace::Thread::Output::cout("\033[32mRunning tscancode on " + file + "\033[0m"); - - bool has_sarif_format = config.global.hasSarifFormat; - std::string src_file = file; - - try + void TscancodeToolImplementation::execute(const std::string& file, ProgramConfig config) const { - std::vector argsProcess; - argsProcess.push_back("--enable=all"); - argsProcess.push_back(src_file); + ctrace::Thread::Output::cout("\033[32mRunning tscancode on " + file + "\033[0m"); - auto process = ProcessFactory::createProcess("./tscancode/src/tscancode/trunk/tscancode", argsProcess); - process->execute(); - ctrace::Thread::Output::tool_out(process->logOutput); - ctrace::Thread::Output::cout("Finished tscancode on " + file); + bool has_sarif_format = config.global.hasSarifFormat; + std::string src_file = file; - if (has_sarif_format) + try { - ctrace::Thread::Output::tool_out(sarifFormat(process->logOutput, "ccoretrace-sarif-tscancode.json").dump()); + std::vector argsProcess; + argsProcess.push_back("--enable=all"); + argsProcess.push_back(src_file); + + auto process = ProcessFactory::createProcess( + "./tscancode/src/tscancode/trunk/tscancode", argsProcess); + process->execute(); + ctrace::Thread::Output::tool_out(process->logOutput); + ctrace::Thread::Output::cout("Finished tscancode on " + file); + + if (has_sarif_format) + { + ctrace::Thread::Output::tool_out( + sarifFormat(process->logOutput, "ccoretrace-sarif-tscancode.json").dump()); + } + // if (has_json_format) + // { + // ctrace::Thread::Output::cout(jsonFormat(process->logOutput, "coretrace-json-tscancode.json").dump()); + // } + } + catch (const std::exception& e) + { + ctrace::Thread::Output::tool_err("Error: " + std::string(e.what())); + return; } - // if (has_json_format) - // { - // ctrace::Thread::Output::cout(jsonFormat(process->logOutput, "coretrace-json-tscancode.json").dump()); - // } - } - catch (const std::exception& e) + + std::string TscancodeToolImplementation::name() const { - ctrace::Thread::Output::tool_err("Error: " + std::string(e.what())); - return; + return "tscancode"; } -} - -std::string TscancodeToolImplementation::name() const -{ - return "tscancode"; -} -std::string_view TscancodeToolImplementation::severityToLevel(const std::string& severity) const -{ - static constexpr std::pair mappings[] = { - {"Warning", "warning"}, - {"Information", "note"}, - {"Error", "error"} - }; - static const std::unordered_map severity_map(mappings, mappings + std::size(mappings)); - - if (const auto it = severity_map.find(severity); it != severity_map.end()) + std::string_view TscancodeToolImplementation::severityToLevel(const std::string& severity) const { - return it->second; + static constexpr std::pair mappings[] = { + {"Warning", "warning"}, {"Information", "note"}, {"Error", "error"}}; + static const std::unordered_map severity_map( + mappings, mappings + std::size(mappings)); + + if (const auto it = severity_map.find(severity); it != severity_map.end()) + { + return it->second; + } + return "none"; } - return "none"; -} -/* + /* START TEST */ -/* + /* END TEST */ -json TscancodeToolImplementation::jsonFormat(const std::string &buffer, const std::string &outputFile) const -{ - json j; - std::cout << buffer << std::endl; - std::cout << outputFile << std::endl; - return j; -} + json TscancodeToolImplementation::jsonFormat(const std::string& buffer, + const std::string& outputFile) const + { + json j; + std::cout << buffer << std::endl; + std::cout << outputFile << std::endl; + return j; + } -json TscancodeToolImplementation::sarifFormat(const std::string &buffer, const std::string &outputFile) const -{ - std::regex diagnostic_regex(R"(\[(.*):(\d+)\]: \((\w+)\) (.*))"); - - json sarif; - sarif["version"] = "2.1.0"; - sarif["$schema"] = "https://json.schemastore.org/sarif-2.1.0.json"; - sarif["runs"] = json::array(); - - json run; - run["tool"]["driver"]["name"] = "coretrace"; - run["tool"]["driver"]["version"] = "1.0.0"; - run["tool"]["driver"]["informationUri"] = "https://coretrace.fr/"; - run["tool"]["driver"]["rules"] = json::array(); - run["results"] = json::array(); - - std::map ruleMap; - int ruleCounter = 0; - - std::istringstream stream(buffer); - std::string line; - - while (std::getline(stream, line)) { - std::smatch match; - if (std::regex_match(line, match, diagnostic_regex)) { - std::string filePath = match[1]; - int lineNumber = std::stoi(match[2]); - std::string severity = match[3]; - std::string message = match[4]; - - std::string ruleId = "coretrace." + severity; - - if (ruleMap.find(ruleId) == ruleMap.end()) { - json rule; - rule["id"] = ruleId; - rule["name"] = severity; - rule["shortDescription"]["text"] = severity + " reported by coretrace"; - run["tool"]["driver"]["rules"].push_back(rule); - ruleMap[ruleId] = ruleCounter++; - } + json TscancodeToolImplementation::sarifFormat(const std::string& buffer, + const std::string& outputFile) const + { + std::regex diagnostic_regex(R"(\[(.*):(\d+)\]: \((\w+)\) (.*))"); + + json sarif; + sarif["version"] = "2.1.0"; + sarif["$schema"] = "https://json.schemastore.org/sarif-2.1.0.json"; + sarif["runs"] = json::array(); - json result; - result["ruleId"] = ruleId; - result["level"] = severityToLevel(severity); - result["message"]["text"] = message; - result["locations"] = {{ - {"physicalLocation", { - {"artifactLocation", {{"uri", filePath}}}, - {"region", {{"startLine", lineNumber}}} - }} - }}; - run["results"].push_back(result); + json run; + run["tool"]["driver"]["name"] = "coretrace"; + run["tool"]["driver"]["version"] = "1.0.0"; + run["tool"]["driver"]["informationUri"] = "https://coretrace.fr/"; + run["tool"]["driver"]["rules"] = json::array(); + run["results"] = json::array(); + + std::map ruleMap; + int ruleCounter = 0; + + std::istringstream stream(buffer); + std::string line; + + while (std::getline(stream, line)) + { + std::smatch match; + if (std::regex_match(line, match, diagnostic_regex)) + { + std::string filePath = match[1]; + int lineNumber = std::stoi(match[2]); + std::string severity = match[3]; + std::string message = match[4]; + + std::string ruleId = "coretrace." + severity; + + if (ruleMap.find(ruleId) == ruleMap.end()) + { + json rule; + rule["id"] = ruleId; + rule["name"] = severity; + rule["shortDescription"]["text"] = severity + " reported by coretrace"; + run["tool"]["driver"]["rules"].push_back(rule); + ruleMap[ruleId] = ruleCounter++; + } + + json result; + result["ruleId"] = ruleId; + result["level"] = severityToLevel(severity); + result["message"]["text"] = message; + result["locations"] = {{{"physicalLocation", + {{"artifactLocation", {{"uri", filePath}}}, + {"region", {{"startLine", lineNumber}}}}}}}; + run["results"].push_back(result); + } } - } - sarif["runs"].push_back(run); + sarif["runs"].push_back(run); - std::ofstream out(outputFile); - out << sarif.dump(4); - return sarif; -} + std::ofstream out(outputFile); + out << sarif.dump(4); + return sarif; + } } // namespace ctrace diff --git a/src/ctrace_tools/languageType.cpp b/src/ctrace_tools/languageType.cpp index 82a48be..d391d0f 100644 --- a/src/ctrace_tools/languageType.cpp +++ b/src/ctrace_tools/languageType.cpp @@ -1,22 +1,28 @@ #include "ctrace_tools/languageType.hpp" -namespace ctrace_tools { +namespace ctrace_tools +{ // Fonction pour détecter le type de langage à partir d'une extension de fichier - [[nodiscard]] ctrace_defs::LanguageType detectLanguage(std::string_view filename) noexcept { + [[nodiscard]] ctrace_defs::LanguageType detectLanguage(std::string_view filename) noexcept + { // Liste des extensions typiques constexpr std::string_view cExtensions[] = {".c", ".h"}; constexpr std::string_view cppExtensions[] = {".cpp", ".hpp", ".cxx", ".cc", ".hxx"}; // Recherche dans les extensions C - for (const auto& ext : cExtensions) { - if (filename.ends_with(ext)) { + for (const auto& ext : cExtensions) + { + if (filename.ends_with(ext)) + { return ctrace_defs::LanguageType::C; } } // Recherche dans les extensions C++ - for (const auto& ext : cppExtensions) { - if (filename.ends_with(ext)) { + for (const auto& ext : cppExtensions) + { + if (filename.ends_with(ext)) + { return ctrace_defs::LanguageType::CPP; } } @@ -24,4 +30,4 @@ namespace ctrace_tools { // Par défaut, on suppose C++ (choix arbitraire, ajustable) return ctrace_defs::LanguageType::CPP; } -} +} // namespace ctrace_tools diff --git a/src/ctrace_tools/mangle.cpp b/src/ctrace_tools/mangle.cpp index ac943ea..d8dc581 100644 --- a/src/ctrace_tools/mangle.cpp +++ b/src/ctrace_tools/mangle.cpp @@ -1,10 +1,10 @@ #include "ctrace_tools/mangle.hpp" -namespace ctrace_tools::mangle { +namespace ctrace_tools::mangle +{ - std::string mangleFunction(const std::string& namespaceName, - const std::string& functionName, - const std::vector& paramTypes) + std::string mangleFunction(const std::string& namespaceName, const std::string& functionName, + const std::vector& paramTypes) { std::stringstream mangled; @@ -22,7 +22,8 @@ namespace ctrace_tools::mangle { mangled << functionName.length() << functionName; // Encoder les types de paramètres - for (const std::string& param : paramTypes) { + for (const std::string& param : paramTypes) + { if (param == "int") { mangled << "i"; @@ -51,8 +52,9 @@ namespace ctrace_tools::mangle { { mangled << "v"; } - else { - // Pour les types complexes ou non reconnus, encoder avec longueur + nom + else + { + // Pour les types complexes ou non reconnus, encoder avec longueur + nom mangled << param.length() << param; } } @@ -66,4 +68,4 @@ namespace ctrace_tools::mangle { return mangled.str(); } -}; +}; // namespace ctrace_tools::mangle diff --git a/src/ctrace_tools/strings.cpp b/src/ctrace_tools/strings.cpp index d48864b..1d0f575 100644 --- a/src/ctrace_tools/strings.cpp +++ b/src/ctrace_tools/strings.cpp @@ -3,49 +3,60 @@ namespace ctrace_tools { -namespace strings -{ - [[nodiscard]] std::vector splitByComma(std::string_view input) noexcept + namespace strings { - std::vector result; - size_t pos = 0; - bool in_quotes = false; - size_t start = 0; + [[nodiscard]] std::vector splitByComma(std::string_view input) noexcept + { + std::vector result; + size_t pos = 0; + bool in_quotes = false; + size_t start = 0; + + while (pos < input.size()) + { + if (input[pos] == '"') + { + in_quotes = !in_quotes; + } + else if (input[pos] == ',' && !in_quotes) + { + std::string_view token = input.substr(start, pos - start); + if (!token.empty() && token.front() == '"' && token.back() == '"') + { + token.remove_prefix(1); + token.remove_suffix(1); + } + token.remove_prefix(std::min(token.find_first_not_of(" "), token.size())); + token.remove_suffix( + std::min(token.size() - token.find_last_not_of(" ") - 1, token.size())); + if (!token.empty()) + { + result.push_back(token); + } + start = pos + 1; + } + ++pos; + } - while (pos < input.size()) { - if (input[pos] == '"') { - in_quotes = !in_quotes; - } else if (input[pos] == ',' && !in_quotes) { - std::string_view token = input.substr(start, pos - start); - if (!token.empty() && token.front() == '"' && token.back() == '"') { + if (start < input.size()) + { + std::string_view token = input.substr(start); + if (!token.empty() && token.front() == '"' && token.back() == '"') + { token.remove_prefix(1); token.remove_suffix(1); } token.remove_prefix(std::min(token.find_first_not_of(" "), token.size())); - token.remove_suffix(std::min(token.size() - token.find_last_not_of(" ") - 1, token.size())); - if (!token.empty()) { + token.remove_suffix( + std::min(token.size() - token.find_last_not_of(" ") - 1, token.size())); + if (!token.empty()) + { result.push_back(token); } - start = pos + 1; } - ++pos; - } - if (start < input.size()) { - std::string_view token = input.substr(start); - if (!token.empty() && token.front() == '"' && token.back() == '"') { - token.remove_prefix(1); - token.remove_suffix(1); - } - token.remove_prefix(std::min(token.find_first_not_of(" "), token.size())); - token.remove_suffix(std::min(token.size() - token.find_last_not_of(" ") - 1, token.size())); - if (!token.empty()) { - result.push_back(token); - } + return result; } + } // namespace strings - return result; - } -} - -} \ No newline at end of file +} // namespace ctrace_tools \ No newline at end of file diff --git a/tests/AvoidDefaultArgumentsOnVirtualMethods.hh b/tests/AvoidDefaultArgumentsOnVirtualMethods.hh index eff40da..b43f8c6 100644 --- a/tests/AvoidDefaultArgumentsOnVirtualMethods.hh +++ b/tests/AvoidDefaultArgumentsOnVirtualMethods.hh @@ -1,13 +1,19 @@ class Base { - public: - virtual void func(int x = 10) { printf("Base: %d\n", x); } // Argument par défaut dans une méthode virtuelle. + public: + virtual void func(int x = 10) + { + printf("Base: %d\n", x); + } // Argument par défaut dans une méthode virtuelle. }; class Derived : public Base { - public: - void func(int x) override { printf("Derived: %d\n", x); } + public: + void func(int x) override + { + printf("Derived: %d\n", x); + } }; int main(void) diff --git a/tests/DestructorOfVirtualClass.hh b/tests/DestructorOfVirtualClass.hh index 9ce66fc..94d9c5b 100644 --- a/tests/DestructorOfVirtualClass.hh +++ b/tests/DestructorOfVirtualClass.hh @@ -1,14 +1,19 @@ -class Base { - public: - ~Base() {} // Destructeur non virtuel dans une classe de base avec des classes dérivées. - }; +class Base +{ + public: + ~Base() {} // Destructeur non virtuel dans une classe de base avec des classes dérivées. +}; - class Derived : public Base { - public: - ~Derived() { /* Libération de ressources */ } - }; +class Derived : public Base +{ + public: + ~Derived() + { /* Libération de ressources */ + } +}; -int main() { +int main() +{ Base* obj = new Derived(); delete obj; // Fuite mémoire : le destructeur de Derived n'est pas appelé. } diff --git a/tests/EmptyForStatement.cc b/tests/EmptyForStatement.cc index 644d191..77a6c0c 100644 --- a/tests/EmptyForStatement.cc +++ b/tests/EmptyForStatement.cc @@ -1,5 +1,7 @@ -int main() { - for (int i = 0; i < 10; i++); // Boucle vide : le point-virgule termine la boucle immédiatement. +int main() +{ + for (int i = 0; i < 10; i++) + ; // Boucle vide : le point-virgule termine la boucle immédiatement. { printf("Hello\n"); } diff --git a/tests/RedundantIfStatement.c b/tests/RedundantIfStatement.c index 7fbfb03..7969a93 100644 --- a/tests/RedundantIfStatement.c +++ b/tests/RedundantIfStatement.c @@ -1,7 +1,11 @@ -bool isPositive(int x_test) { - if (x_test > 0) { +bool isPositive(int x_test) +{ + if (x_test > 0) + { return true; // Condition redondante : peut être simplifiée. - } else { + } + else + { return false; } // Mieux : return x > 0; diff --git a/tests/RedundantIfStatement.cc b/tests/RedundantIfStatement.cc index 7fbfb03..7969a93 100644 --- a/tests/RedundantIfStatement.cc +++ b/tests/RedundantIfStatement.cc @@ -1,7 +1,11 @@ -bool isPositive(int x_test) { - if (x_test > 0) { +bool isPositive(int x_test) +{ + if (x_test > 0) + { return true; // Condition redondante : peut être simplifiée. - } else { + } + else + { return false; } // Mieux : return x > 0; diff --git a/tests/bad_function_pointer.cc b/tests/bad_function_pointer.cc index 54534b8..6ffda92 100644 --- a/tests/bad_function_pointer.cc +++ b/tests/bad_function_pointer.cc @@ -2,16 +2,19 @@ typedef void (*FuncPtr)(int); -void myFunction(int x) { +void myFunction(int x) +{ printf("Value: %d\n", x); } -void anotherFunction(double y) { +void anotherFunction(double y) +{ printf("Double value: %f\n", y); } -int main() { +int main() +{ FuncPtr fptr = (FuncPtr)anotherFunction; // Mauvaise conversion de type - fptr(42); // Appel incorrect : un int est interprété comme un double + fptr(42); // Appel incorrect : un int est interprété comme un double return 0; } diff --git a/tests/bound_index.cc b/tests/bound_index.cc index 5427c94..c1862dd 100644 --- a/tests/bound_index.cc +++ b/tests/bound_index.cc @@ -2,10 +2,10 @@ int a[10]; -int main(int argc, char *argv[]) +int main(int argc, char* argv[]) { size_t i = 0; - for (;i < 10; i++) + for (; i < 10; i++) { a[i] = i; } diff --git a/tests/buffer_overflow.cc b/tests/buffer_overflow.cc index 9eb922e..9396932 100644 --- a/tests/buffer_overflow.cc +++ b/tests/buffer_overflow.cc @@ -1,18 +1,21 @@ #include #include -void vulnerable_function(char *input) { - char buffer[8]; // Un petit buffer de 8 octets +void vulnerable_function(char* input) +{ + char buffer[8]; // Un petit buffer de 8 octets printf("Adresse du buffer : %p\n", (void*)buffer); - + // Copie de l'input sans vérification -> peut provoquer un buffer overflow strcpy(buffer, input); printf("Contenu du buffer : %s\n", buffer); } -int main(int argc, char *argv[]) { - if (argc < 2) { +int main(int argc, char* argv[]) +{ + if (argc < 2) + { printf("Usage: %s \n", argv[0]); return 1; } diff --git a/tests/dead_code.cc b/tests/dead_code.cc index b96942c..5f492d3 100644 --- a/tests/dead_code.cc +++ b/tests/dead_code.cc @@ -1,7 +1,8 @@ int compute(int x) { int y = x + 1; - if (x > 0) { + if (x > 0) + { return x * 2; } return x * 3; // Code mort : jamais atteint car toutes les branches retournent avant. @@ -10,7 +11,8 @@ int compute(int x) int over_run_compute(int x) { int y = x + 1; - if (x > 0) { + if (x > 0) + { return x * 2; // Code mort : jamais atteint car toutes les branches retournent avant. } return x * 3; @@ -20,13 +22,15 @@ int single_compute(void) { int x = 5; - if (x > 0) { + if (x > 0) + { return 2; // Code mort : jamais atteint car toutes les branches retournent avant. } return 3; } -int main() { +int main() +{ int a = 5; int b = compute(a); a = -5; diff --git a/tests/double_free.c b/tests/double_free.c index a09cc3d..7370462 100644 --- a/tests/double_free.c +++ b/tests/double_free.c @@ -3,11 +3,12 @@ int main(void) { - char* ptr = (char*)malloc(10); // Allocation de 10 octets - if (ptr == NULL) return 1; // Vérification basique + char* ptr = (char*)malloc(10); // Allocation de 10 octets + if (ptr == NULL) + return 1; // Vérification basique - free(ptr); // Première libération - free(ptr); // Deuxième libération (double free) + free(ptr); // Première libération + free(ptr); // Deuxième libération (double free) return 0; } diff --git a/tests/format_problem.c b/tests/format_problem.c index acb3543..54e2129 100644 --- a/tests/format_problem.c +++ b/tests/format_problem.c @@ -1,6 +1,6 @@ #include -void print_input(char *input) +void print_input(char* input) { printf(input); } diff --git a/tests/if_collapse.c b/tests/if_collapse.c index 66c930f..1520fe1 100644 --- a/tests/if_collapse.c +++ b/tests/if_collapse.c @@ -1,9 +1,11 @@ -int main() { +int main() +{ int iii = 0, jjj = 1; if (jjj) { - if (iii) { + if (iii) + { jjj = 0; return 1; } diff --git a/tests/if_constant_expr.c b/tests/if_constant_expr.c index a78ad15..4eb067d 100644 --- a/tests/if_constant_expr.c +++ b/tests/if_constant_expr.c @@ -1,4 +1,5 @@ -int main() { +int main() +{ const int c_test = 1; if (c_test) diff --git a/tests/null_pointer.c b/tests/null_pointer.c index 4502b5b..057a20d 100644 --- a/tests/null_pointer.c +++ b/tests/null_pointer.c @@ -3,20 +3,21 @@ int toto(void) { - int* ptr = NULL; // Pointeur initialisé à NULL - *ptr = 42; // Déréférencement de pointeur nul + int* ptr = NULL; // Pointeur initialisé à NULL + *ptr = 42; // Déréférencement de pointeur nul return 0; } int main(void) { - int* ptr = (int*)malloc(sizeof(int)); // Allocation qui peut échouer + int* ptr = (int*)malloc(sizeof(int)); // Allocation qui peut échouer - if (ptr != NULL) { - *ptr = 10; // Accès valide si l'allocation réussit + if (ptr != NULL) + { + *ptr = 10; // Accès valide si l'allocation réussit } - *ptr = 42; // Déréférencement potentiel de NULL si malloc échoue + *ptr = 42; // Déréférencement potentiel de NULL si malloc échoue free(ptr); toto(); return 0; diff --git a/tests/partitioning.cc b/tests/partitioning.cc index e46c4de..a839a0f 100644 --- a/tests/partitioning.cc +++ b/tests/partitioning.cc @@ -1,41 +1,50 @@ // init_with_error.c #include -int* xxx() { - return (int*)malloc(sizeof(int)); // Allocation réussie ou NULL +int* xxx() +{ + return (int*)malloc(sizeof(int)); // Allocation réussie ou NULL } -int yyy(int* ptr) { - if (ptr == NULL) return -1; // Erreur si ptr est NULL - *ptr = 42; //Ascending; // Déréférencement potentiel de NULL +int yyy(int* ptr) +{ + if (ptr == NULL) + return -1; // Erreur si ptr est NULL + *ptr = 42; //Ascending; // Déréférencement potentiel de NULL return 0; } -int zzz(int* ptr) { - *ptr = 100; // Déréférencement potentiel de NULL +int zzz(int* ptr) +{ + *ptr = 100; // Déréférencement potentiel de NULL return 0; } -int init(void) { - int* ptr = xxx(); // Peut retourner NULL - if (ptr == NULL) { - return -1; // Erreur dans xxx +int init(void) +{ + int* ptr = xxx(); // Peut retourner NULL + if (ptr == NULL) + { + return -1; // Erreur dans xxx } int status = yyy(ptr); - if (status < 0) { + if (status < 0) + { free(ptr); - return -2; // Erreur dans yyy + return -2; // Erreur dans yyy } zzz(ptr); free(ptr); - return 0; // Succès + return 0; // Succès } -int main(void) { +int main(void) +{ int result = init(); - if (result == 0) { + if (result == 0) + { return 1; } return 0; diff --git a/tests/pointer_comparison_analysis.cc b/tests/pointer_comparison_analysis.cc index 07357ac..1cfdc95 100644 --- a/tests/pointer_comparison_analysis.cc +++ b/tests/pointer_comparison_analysis.cc @@ -2,28 +2,35 @@ #include #include -void bad_comparison(int *p, int *q) +void bad_comparison(int* p, int* q) { - if (p < q) { // Comparaison entre pointeurs d'objets différents + if (p < q) + { // Comparaison entre pointeurs d'objets différents printf("p < q\n"); - } else { + } + else + { printf("p >= q\n"); } } int main(void) { - int* p = (int*)malloc(sizeof(int)); // Première allocation - int* q = (int*)malloc(sizeof(int)); // Deuxième allocation (objet différent) + int* p = (int*)malloc(sizeof(int)); // Première allocation + int* q = (int*)malloc(sizeof(int)); // Deuxième allocation (objet différent) - if (p == NULL || q == NULL) return 1; // Vérification basique + if (p == NULL || q == NULL) + return 1; // Vérification basique *p = 10; *q = 20; - if (p < q) { // Comparaison entre pointeurs d'objets différents + if (p < q) + { // Comparaison entre pointeurs d'objets différents printf("p < q\n"); - } else { + } + else + { printf("p >= q\n"); } bad_comparison(p, q); diff --git a/tests/pointer_overflow.cc b/tests/pointer_overflow.cc index 8489f40..71042a1 100644 --- a/tests/pointer_overflow.cc +++ b/tests/pointer_overflow.cc @@ -3,10 +3,12 @@ int main(void) { int* array = (int*)malloc(5 * sizeof(int)); - if (array == NULL) return 1; + if (array == NULL) + return 1; int* ptr = array; - for (int i = 0; i < 10; i++) { - ptr = ptr + 1; // Déborde après i = 5 + for (int i = 0; i < 10; i++) + { + ptr = ptr + 1; // Déborde après i = 5 *ptr = i; } free(array); diff --git a/tests/too_many_methods.cc b/tests/too_many_methods.cc index be77166..663616d 100644 --- a/tests/too_many_methods.cc +++ b/tests/too_many_methods.cc @@ -1,14 +1,15 @@ -class MyClass { - public: - void method1() {} - void method2() {} - void method3() {} - void method4() {} - void method5() {} - void method6() {} - void method7() {} - void method8() {} - void method9() {} - void method10() {} - // Trop de méthodes : la classe devient difficile à maintenir. - }; \ No newline at end of file +class MyClass +{ + public: + void method1() {} + void method2() {} + void method3() {} + void method4() {} + void method5() {} + void method6() {} + void method7() {} + void method8() {} + void method9() {} + void method10() {} + // Trop de méthodes : la classe devient difficile à maintenir. +}; \ No newline at end of file diff --git a/tests/unaligned_dereferencing.cc b/tests/unaligned_dereferencing.cc index 0aead1e..2794760 100644 --- a/tests/unaligned_dereferencing.cc +++ b/tests/unaligned_dereferencing.cc @@ -3,11 +3,11 @@ int main(void) { - char buffer[8] = {0}; // Buffer de 8 octets - char* ptr = buffer + 1; // Pointe vers une adresse non alignée (offset de 1) - int* unaligned = (int*)ptr; // Conversion en pointeur int (taille 4 octets) + char buffer[8] = {0}; // Buffer de 8 octets + char* ptr = buffer + 1; // Pointe vers une adresse non alignée (offset de 1) + int* unaligned = (int*)ptr; // Conversion en pointeur int (taille 4 octets) - *unaligned = 42; // Déréférencement non aligné + *unaligned = 42; // Déréférencement non aligné printf("Value: %d\n", *unaligned); return 0; } From bda6701a689b7fa5b2656a3ebd1c92bafac1028b Mon Sep 17 00:00:00 2001 From: Hugo Date: Fri, 30 Jan 2026 15:16:04 +0100 Subject: [PATCH 4/5] fix(CI-format): compliance to clang-format 17 --- include/Process/ThreadProcess.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/Process/ThreadProcess.hpp b/include/Process/ThreadProcess.hpp index fac109f..6678429 100644 --- a/include/Process/ThreadProcess.hpp +++ b/include/Process/ThreadProcess.hpp @@ -112,7 +112,7 @@ namespace ctrace emit_line("stderr", message, std::cerr, true); } } // namespace Output - } // namespace Thread + } // namespace Thread } // namespace ctrace #endif // THREAD_PROCESS_HPP From a9847a7d0929acc90ce3f01d4d6cdfa6a35f6510 Mon Sep 17 00:00:00 2001 From: Hugo Date: Fri, 30 Jan 2026 15:19:51 +0100 Subject: [PATCH 5/5] style: fix formatting for clang-format ci --- include/Process/ThreadProcess.hpp | 2 +- src/Process/Tools/StackAnalyzerToolImplementation.cpp | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/include/Process/ThreadProcess.hpp b/include/Process/ThreadProcess.hpp index 6678429..fac109f 100644 --- a/include/Process/ThreadProcess.hpp +++ b/include/Process/ThreadProcess.hpp @@ -112,7 +112,7 @@ namespace ctrace emit_line("stderr", message, std::cerr, true); } } // namespace Output - } // namespace Thread + } // namespace Thread } // namespace ctrace #endif // THREAD_PROCESS_HPP diff --git a/src/Process/Tools/StackAnalyzerToolImplementation.cpp b/src/Process/Tools/StackAnalyzerToolImplementation.cpp index c1e99bd..d0f5376 100644 --- a/src/Process/Tools/StackAnalyzerToolImplementation.cpp +++ b/src/Process/Tools/StackAnalyzerToolImplementation.cpp @@ -41,8 +41,7 @@ namespace ctrace "void"); // dummy to avoid empty vector issue // refaire avec les paramèters réels // llvm::outs() << "Function: " << f.name << " " << ((ctrace::stack::isMangled(f.name)) ? ctrace::stack::demangle(f.name.c_str()) : "") << "\n"; - llvm::outs() << "Function: " << f.name << " " - << "\n"; + llvm::outs() << "Function: " << f.name << " " << "\n"; llvm::outs() << " local stack: " << f.localStack << " bytes\n"; llvm::outs() << " max stack (including callees): " << f.maxStack << " bytes\n";