File indexing completed on 2024-12-08 04:27:18

0001 /*
0002    SPDX-FileCopyrightText: 2019 Nicolas Carion
0003    SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
0004 
0005     This file is part of Kdenlive. See www.kdenlive.org.
0006 */
0007 
0008 #pragma once
0009 #include <climits>
0010 #include <iostream>
0011 #include <memory>
0012 #include <mutex>
0013 #include <unordered_map>
0014 
0015 #pragma GCC diagnostic push
0016 #pragma GCC diagnostic ignored "-Wunused-parameter"
0017 #pragma GCC diagnostic ignored "-Wsign-conversion"
0018 #pragma GCC diagnostic ignored "-Wfloat-equal"
0019 #pragma GCC diagnostic ignored "-Wshadow"
0020 #pragma GCC diagnostic ignored "-Wpedantic"
0021 #include <rttr/variant.h>
0022 #pragma GCC diagnostic pop
0023 
0024 /** @brief This class is meant to provide an easy way to reproduce bugs involving the model.
0025  * The idea is to log any modifier function involving a model class, and trace the parameters that were passed, to be able to generate a test-case producing the
0026  * same behaviour. Note that many modifier functions of the models are nested. We are only interested in the top-most call, and we must ignore bottom calls.
0027  */
0028 class Logger
0029 {
0030 public:
0031     /** @brief Inits the logger. Must be called at startup */
0032     static void init();
0033 
0034     /** @brief Notify the logger that the current thread wants to start logging.
0035      * This function returns true if this is a top-level call, meaning that we indeed want to log it. If the function returns false, the  caller must not log.
0036      */
0037     static bool start_logging();
0038 
0039     /** @brief This logs the construction of an object of type T, whose new instance is passed. The instance will be kept around in case future calls refer to
0040      * it. The arguments should more or less match the constructor arguments. In general, it's better to call the corresponding macro TRACE_CONSTR */
0041     template <typename T> static void log_constr(T *inst, std::vector<rttr::variant> args);
0042 
0043     /** @brief Logs the call to a member function on a given instance of class T. The string contains the method name, and then the vector contains all the
0044      * parameters. In general, the method should be registered in RTTR. It's better to call the corresponding macro TRACE() if appropriate */
0045     template <typename T> static void log(T *inst, std::string str, std::vector<rttr::variant> args);
0046     static void log_create_producer(const std::string &type, std::vector<rttr::variant> args);
0047 
0048     /** @brief When the last function logged has a return value, you can log it through this function, by passing the corresponding value. In general, it's
0049      * better to call the macro TRACE_RES */
0050     static void log_res(rttr::variant result);
0051 
0052     // log whenever an undo/redo occurred
0053     static void log_undo(bool undo);
0054 
0055     /** @brief Notify that we are done with our function. Must not be called if start_logging returned false. */
0056     static void stop_logging();
0057     static void print_trace();
0058 
0059     /** @brief Resets the current log */
0060     static void clear();
0061 
0062     static std::unordered_map<std::string, std::string> translation_table;
0063     static std::unordered_map<std::string, std::string> back_translation_table;
0064 
0065 protected:
0066     /** @brief Look amongst the known instances to get the name of a given pointer */
0067     static std::string get_ptr_name(const rttr::variant &ptr);
0068     template <typename T> static size_t get_id_from_ptr(T *ptr);
0069     struct InvokId
0070     {
0071         size_t id;
0072     };
0073     struct ConstrId
0074     {
0075         std::string type;
0076         size_t id;
0077     };
0078     struct Undo
0079     {
0080         bool undo;
0081     };
0082     // a construction log contains the pointer as first parameter, and the vector of parameters
0083     using Constr = std::pair<rttr::variant, std::vector<rttr::variant>>;
0084     struct Invok
0085     {
0086         rttr::variant ptr;
0087         std::string method;
0088         std::vector<rttr::variant> args;
0089         rttr::variant res;
0090     };
0091     thread_local static bool is_executing;
0092     thread_local static size_t result_awaiting;
0093     static std::mutex mut;
0094     static std::vector<rttr::variant> operations;
0095     static std::unordered_map<std::string, std::vector<Constr>> constr;
0096     static std::vector<Invok> invoks;
0097     static int dump_count;
0098 };
0099 
0100 /** @brief This class provides a RAII mechanism to log the execution of a function */
0101 class LogGuard
0102 {
0103 public:
0104     LogGuard();
0105     ~LogGuard();
0106     /** @brief Returns true if we are the top-level caller. */
0107     bool hasGuard() const;
0108 
0109 protected:
0110     bool m_hasGuard = false;
0111 };
0112 
0113 /// See Logger::log_constr. Note that the macro fills in the ptr instance for you.
0114 #define TRACE_CONSTR(ptr, ...)                                                                                                                                 \
0115     LogGuard __guard;                                                                                                                                          \
0116     if (__guard.hasGuard()) {                                                                                                                                  \
0117         Logger::log_constr((ptr), {__VA_ARGS__});                                                                                                              \
0118     }
0119 
0120 /// See Logger::log. Note that the macro fills the ptr instance and the method name for you.
0121 #define TRACE(...)                                                                                                                                             \
0122     LogGuard __guard;                                                                                                                                          \
0123     if (__guard.hasGuard()) {                                                                                                                                  \
0124         Logger::log(this, __FUNCTION__, {__VA_ARGS__});                                                                                                        \
0125     }
0126 
0127 /// Same as TRACE, but called from a static function
0128 #define TRACE_STATIC(ptr, ...)                                                                                                                                 \
0129     LogGuard __guard;                                                                                                                                          \
0130     if (__guard.hasGuard()) {                                                                                                                                  \
0131         Logger::log(ptr.get(), __FUNCTION__, {__VA_ARGS__});                                                                                                   \
0132     }
0133 
0134 /// See Logger::log_res
0135 #define TRACE_RES(res)                                                                                                                                         \
0136     if (__guard.hasGuard()) {                                                                                                                                  \
0137         Logger::log_res(res);                                                                                                                                  \
0138     }
0139 
0140 /******* Implementations ***********/
0141 template <typename T> void Logger::log_constr(T *inst, std::vector<rttr::variant> args)
0142 {
0143     std::unique_lock<std::mutex> lk(mut);
0144     for (auto &a : args) {
0145         // this will rewove shared/weak/unique ptrs
0146         if (a.get_type().is_wrapper()) {
0147             a = a.extract_wrapped_value();
0148         }
0149     }
0150     std::string class_name = rttr::type::get<T>().get_name().to_string();
0151     constr[class_name].push_back({inst, std::move(args)});
0152     operations.emplace_back(ConstrId{class_name, constr[class_name].size() - 1});
0153 }
0154 
0155 template <typename T> void Logger::log(T *inst, std::string fctName, std::vector<rttr::variant> args)
0156 {
0157     std::unique_lock<std::mutex> lk(mut);
0158     for (auto &a : args) {
0159         // this will rewove shared/weak/unique ptrs
0160         if (a.get_type().is_wrapper()) {
0161             a = a.extract_wrapped_value();
0162         }
0163     }
0164     std::string class_name = rttr::type::get<T>().get_name().to_string();
0165     invoks.push_back({inst, std::move(fctName), std::move(args), rttr::variant()});
0166     operations.emplace_back(InvokId{invoks.size() - 1});
0167     result_awaiting = invoks.size() - 1;
0168 }
0169 
0170 template <typename T> size_t Logger::get_id_from_ptr(T *ptr)
0171 {
0172     const std::string class_name = rttr::type::get<T>().get_name().to_string();
0173     for (size_t i = 0; i < constr.at(class_name).size(); ++i) {
0174         if (constr.at(class_name)[i].first.convert<T *>() == ptr) {
0175             return i;
0176         }
0177     }
0178     std::cerr << "Error: ptr of type " << class_name << " not found" << std::endl;
0179     return INT_MAX;
0180 }