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 }