/**************************************************************************** * * Copyright (c) 2020-2023 Vivante Corporation * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. * *****************************************************************************/ #ifndef TIM_EXPERIMENTAL_TRACE_TRACER_H_ #define TIM_EXPERIMENTAL_TRACE_TRACER_H_ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define TRACE_LOG_NAME_ "trace_log.cc" #define TRACE_BIN_FILE_ "trace_bin.bin" #define TRACE_LOG_FILE_REPLAY_ "trace_log.rpl.cc" #define TRACE_BIN_FILE_REPLAY_ "trace_bin.rpl.bin" #define TRACE_PREFIX_ENV_VAR_ "TRACE_DUMP_PREFIX" #define TCLOGE(fmt, ...) do { \ printf("[ERROR] [%s:%s:%d]" fmt, __FILE__, __FUNCTION__, __LINE__, \ ##__VA_ARGS__); \ fflush(stdout); \ } while (0) /********************** definitions of extra type traits **********************/ namespace trace { template using void_t = void; template struct is_fundamental_vector : std::false_type {}; template struct is_fundamental_vector> { static constexpr bool value = std::is_fundamental::value; }; template struct is_fundamental_array : std::false_type {}; template struct is_fundamental_array> { static constexpr bool value = std::is_fundamental::value; }; template struct is_fundamental_pointer : std::integral_constant::value && std::is_fundamental>::value> {}; template struct is_traced_obj : std::false_type {}; template struct is_traced_obj().TraceGetObjName())>> : std::true_type {}; template struct is_traced_obj_ptr : std::false_type {}; template struct is_traced_obj_ptr()->TraceGetObjName())>> : std::true_type {}; template struct is_traced_obj_ptr_vector : std::false_type {}; template struct is_traced_obj_ptr_vector> { static constexpr bool value = is_traced_obj_ptr::value; }; template struct is_others_type : std::integral_constant>::value && !is_fundamental_array>::value && !std::is_enum>::value && !std::is_fundamental>::value && !is_traced_obj>::value && !is_traced_obj_ptr>::value && !is_traced_obj_ptr_vector>::value> {}; template struct is_not_traced_obj_like : std::integral_constant>::value && !is_traced_obj_ptr>::value && !is_traced_obj_ptr_vector>::value> {}; } /* namespace trace */ /*************************** definition of replayer ***************************/ namespace trace { class Replayer { static FILE* file_trace_bin_; static FILE* open_file(const char* file_name); public: template static std::vector get_vector(uint32_t offset, size_t vec_size); template static std::vector get_vector_from( const char* file_path, uint32_t offset, size_t vec_size); template static std::array get_array(uint32_t offset, size_t vec_size); }; // #define API_REPLAYER_IMPLEMENTATION #ifdef API_REPLAYER_IMPLEMENTATION FILE* Replayer::file_trace_bin_ = Replayer::open_file(TRACE_BIN_FILE_REPLAY_); /* static */ FILE* Replayer::open_file(const char* file_name) { char* prefix = getenv(TRACE_PREFIX_ENV_VAR_); FILE* fp; char path[1024] = {0}; if (prefix != NULL) { strcpy(path, prefix); strcat(path, file_name); } else { strcpy(path, file_name); } fp = fopen(path, "r"); if (!fp) { TCLOGE("Can not open file at: %s\n", path); } return fp; } template /* static */ std::vector Replayer::get_vector( uint32_t offset, size_t vec_size) { std::vector ret_vec; if (!file_trace_bin_) { TCLOGE("FILE pointer is NULL!\n"); } else { T* buffer = new T[vec_size]; fseek(file_trace_bin_, offset, SEEK_SET); if (fread(buffer, sizeof(T), vec_size, file_trace_bin_) == vec_size) { ret_vec.assign(buffer, buffer + vec_size); } else { TCLOGE("Read binary data failed!\n"); } delete[] buffer; } return ret_vec; } template /* static */ std::array Replayer::get_array( uint32_t offset, size_t vec_size) { std::vector ret_vec = get_vector(offset, vec_size); std::array ret_arr; std::copy_n(ret_vec.begin(), ret_vec.size(), ret_arr.begin()); return ret_arr; } template /* static */ std::vector Replayer::get_vector_from( const char* file_path,uint32_t offset, size_t vec_size) { FILE* external_file = fopen(file_path, "r"); if (!external_file) { TCLOGE("Can not open file at: %s\n", file_path); } std::vector ret_vec; T* buffer = new T[vec_size]; fseek(external_file, offset, SEEK_SET); if (fread(buffer, sizeof(T), vec_size, external_file) == vec_size) { ret_vec.assign(buffer, buffer + vec_size); } else { TCLOGE("Read binary data failed!\n"); } delete[] buffer; return ret_vec; } #endif /* #ifdef API_TRACER_IMPLEMENTATION */ } /* namespace trace */ /**************************** definition of tracer ****************************/ namespace trace { class Tracer { static std::unordered_map obj_names_; static std::unordered_map objs_prefix_; static std::vector params_log_cache_; static std::list msg_cache_; static std::unordered_map target2trace_map_; static FILE* file_trace_log_; static FILE* file_trace_bin_; static std::mutex log_mtx_; static std::mutex bin_mtx_; static FILE* open_file(const char* file_name); public: static void logging_msg(const char* format, ...); static uint32_t dump_data(const void* data, size_t byte_size, size_t count); static std::string allocate_obj_name(const std::string& prefix = "obj_"); static inline void insert_obj_name( const void* obj_ptr, const std::string& obj_name) { obj_names_[obj_ptr] = obj_name; } static inline void insert_traced_obj(const void* p_target, void* p_traced) { target2trace_map_.insert({p_target, p_traced}); } static inline void* get_traced_obj(const void* p_target) { return target2trace_map_[p_target]; } static inline std::string& get_obj_name(const void* obj) { return obj_names_[obj]; } static inline std::string& get_obj_prefix(const std::string cls_name) { return objs_prefix_[cls_name]; } static inline void push_back_msg_cache(const std::string& msg) { msg_cache_.push_back(msg); } static inline void amend_last_msg_cache(const std::string& msg) { if (msg_cache_.empty()) { TCLOGE("Can't amend sub_msg, because msg cache is empty!\n"); } msg_cache_.back() += msg; } static inline void insert_before_last_msg_cache(const std::string& msg) { msg_cache_.insert(--msg_cache_.end(), msg); } static inline void msg_cache_sync_to_file() { while (!msg_cache_.empty()) { logging_msg(msg_cache_.front().c_str()); msg_cache_.pop_front(); } } static inline void clear_params_log_cache() { params_log_cache_.clear(); } static inline void init_params_log_cache(uint32_t params_size) { params_log_cache_.clear(); params_log_cache_.resize(params_size); } static inline void append_params_log_cache(std::string param_log) { params_log_cache_.push_back(param_log); } static inline void insert_params_log_cache( std::string param_log, uint32_t idx) { if (idx != static_cast(-1)) { params_log_cache_[idx] = param_log; } else { params_log_cache_.push_back(param_log); } } // pop the log of params into msg cache static inline void pop_params_log_cache() { if (params_log_cache_.size() == 0) return; for (uint32_t i = 0; i < params_log_cache_.size() - 1; i++) { amend_last_msg_cache(params_log_cache_[i] + ", "); } amend_last_msg_cache(params_log_cache_.back()); } // directly dump the log of params to file static inline void dump_params_log_cache() { if (params_log_cache_.size() == 0) return; for (uint32_t i = 0; i < params_log_cache_.size() - 1; i++) { logging_msg("%s, ", params_log_cache_[i].c_str()); } logging_msg(params_log_cache_.back().c_str()); } /* * template functions for logging parameters to log file */ // default substitution #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wunused-parameter" template ::value, int> = 0> static inline void logging_param(const T& t, uint32_t idx) { insert_params_log_cache(std::string(), idx); } #pragma GCC diagnostic pop // enable if T is fundamental std::vector template >::value, int> = 0> static inline void logging_param(const T& t, uint32_t idx) { uint32_t offset = dump_data(t.data(), sizeof(t[0]), t.size()); std::string element_type = boost::typeindex::type_id().pretty_name(); char log_msg[1024] = {0}; snprintf(log_msg, 1024, "trace::Replayer::get_vector<%s>(%u, %u)", element_type.c_str(), offset, (uint32_t)t.size()); insert_params_log_cache(std::string(log_msg), idx); } // enable if T is fundamental std::array template >::value, int> = 0> static inline void logging_param(const T& t, uint32_t idx) { uint32_t offset = dump_data(t.data(), sizeof(t[0]), t.size()); std::string element_type = boost::typeindex::type_id().pretty_name(); char log_msg[1024] = {0}; snprintf(log_msg, 1024, "trace::Replayer::get_array<%s, %d>(%u, %u)", element_type.c_str(), (uint32_t)t.size(), offset, (uint32_t)t.size()); insert_params_log_cache(std::string(log_msg), idx); } // enable if T is enum template >::value, int> = 0> static inline void logging_param(const T& t, uint32_t idx) { std::string enum_type = boost::typeindex::type_id().pretty_name(); char log_msg[1024] = {0}; snprintf(log_msg, 1024, "(%s)%d", enum_type.c_str(), (int)t); insert_params_log_cache(std::string(log_msg), idx); } // enable if T is fundamental template >::value, int> = 0> static inline void logging_param(const T& t, uint32_t idx) { insert_params_log_cache(std::to_string(t), idx); } // enable if T is derive from TraceClassBase template >::value, int> = 0> static inline void logging_param(const T& t, uint32_t idx) { insert_params_log_cache(t.TraceGetObjName(), idx); } // enable if T is pointer to object which // derive from TraceClassBase template >::value, int> = 0> static inline void logging_param(const T& t, uint32_t idx) { insert_params_log_cache(t->TraceGetObjName(), idx); } // enable if T is vector of pointer to object which // derive from TraceClassBase template >::value, int> = 0> static inline void logging_param(const T& t, uint32_t idx) { std::stringstream ss; ss << "{"; for (uint32_t i = 0; i < t.size() - 1; i++) { ss << t[i]->TraceGetObjName() << ", "; } ss << t.back()->TraceGetObjName() << "}"; insert_params_log_cache(ss.str(), idx); } /* * template functions for pass correct parameters to api implementation */ // default substitution template ::value, int> = 0> static inline T&& proc_param(T&& t) { return std::forward(t); } // enable if T is derive from TraceClassBase template >::value, int> = 0> static inline decltype(std::declval().TraceGetImpl())&& proc_param( T&& t) { return std::forward(t).TraceGetImpl(); } // enable if T is pointer to object which // derive from TraceClassBase template >::value, int> = 0> static inline decltype(std::declval()->TraceGetImplSp()) proc_param( T&& t) { return std::forward(t)->TraceGetImplSp(); } // enable if T is std::vector which element is pointer of traced object template >::value, int> = 0> static inline std::vector()[0]->TraceGetImplSp())> proc_param(T&& t) { std::vectorTraceGetImplSp())> impl_vec; for (auto& x : std::forward(t)) { impl_vec.emplace_back(x->TraceGetImplSp()); } return impl_vec; } }; #ifdef API_TRACER_IMPLEMENTATION std::unordered_map Tracer::obj_names_; std::vector Tracer::params_log_cache_; std::list Tracer::msg_cache_; std::unordered_map Tracer::target2trace_map_; FILE* Tracer::file_trace_log_ = Tracer::open_file(TRACE_LOG_NAME_); FILE* Tracer::file_trace_bin_ = Tracer::open_file(TRACE_BIN_FILE_); std::mutex Tracer::log_mtx_; std::mutex Tracer::bin_mtx_; std::unordered_map Tracer::objs_prefix_ = { {"Quantization", "quant_" }, {"TensorSpec", "spec_" }, {"Tensor", "tensor_" }, {"Graph", "graph_" } }; /* static */ FILE* Tracer::open_file(const char* file_name) { char* prefix = getenv(TRACE_PREFIX_ENV_VAR_); FILE* fp; char path[1024] = {0}; if (prefix != NULL) { strcpy(path, prefix); strcat(path, file_name); } else { strcpy(path, file_name); } fp = fopen(path, "w"); if (!fp) { TCLOGE("Can not open file at: %s\n", path); } return fp; } /* static */ std::string Tracer::allocate_obj_name( const std::string& prefix) { static std::unordered_map objects_counter; if (objects_counter.find(prefix) == objects_counter.end()) { objects_counter[prefix] = 0; } return prefix + std::to_string(objects_counter[prefix]++); } /* static */ void Tracer::logging_msg(const char* format, ...) { char arg_buffer[1024] = {0}; va_list args; va_start(args, format); vsnprintf(arg_buffer, 1024, format, args); va_end(args); // printf("%s", arg_buffer); if (!file_trace_log_) { TCLOGE("Log file do not exist\n"); } std::lock_guard lock(log_mtx_); fprintf(file_trace_log_, "%s", arg_buffer); fflush(file_trace_log_); } /* static */ uint32_t Tracer::dump_data( const void* data, size_t byte_size, size_t count) { std::lock_guard lock(bin_mtx_); if (fwrite(data, byte_size, count, file_trace_bin_) != count) { TCLOGE("Write trace binary data failed!\n"); } fflush(file_trace_bin_); static uint32_t offset = 0; uint32_t temp = offset; offset += byte_size * count; return temp; } #endif /* #ifdef API_TRACER_IMPLEMENTATION */ } /* namespace trace */ /************************ definition of TraceClassBase ************************/ namespace trace { template struct TraceClassBase { TargetClass& TraceGetImpl() const { return *impl_; } // temporary return rvalue to prevent compile error std::shared_ptr TraceGetImplSp() { return impl_; } std::string& TraceGetObjName() const { return Tracer::get_obj_name(static_cast(impl_.get())); } std::shared_ptr impl_; // static const char* target_namespace_name_; }; // #ifdef TARGET_NAMESPACE_NAME // template // const char* TraceClassBase::target_namespace_name_ = // TARGET_NAMESPACE_NAME; // #endif // #ifdef TARGET_NAMESPACE_NAME } /* namespace trace */ #ifdef TARGET_NAMESPACE_NAME static const char* target_namespace_name_ = TARGET_NAMESPACE_NAME; #endif // #ifdef TARGET_NAMESPACE_NAME /**************************** definitions of macros ***************************/ #define LOG_PARAM_IMPL_(r, _, i, param) \ Tracer::logging_param(param, i); #define LOG_PARAMS(params) \ Tracer::init_params_log_cache(BOOST_PP_SEQ_SIZE(params)); \ BOOST_PP_SEQ_FOR_EACH_I(LOG_PARAM_IMPL_, _, params) #define PROC_PARAM_IMPL_COMMA_(r, _, param) \ Tracer::proc_param(param), #define PROC_PARAM_IMPL_NO_COMMA_(param) \ Tracer::proc_param(param) #define PROC_SINGLE_PARAM_(params) \ PROC_PARAM_IMPL_NO_COMMA_(BOOST_PP_SEQ_ELEM(0, params)) #define PROC_MULTI_PARAMS_(params) \ BOOST_PP_SEQ_FOR_EACH( \ PROC_PARAM_IMPL_COMMA_, _, \ BOOST_PP_SEQ_SUBSEQ(params, 0, BOOST_PP_DEC(BOOST_PP_SEQ_SIZE(params)))) \ PROC_PARAM_IMPL_NO_COMMA_( \ BOOST_PP_SEQ_ELEM(BOOST_PP_DEC(BOOST_PP_SEQ_SIZE(params)), params)) #define PROC_PARAMS(params) \ BOOST_PP_IF(BOOST_PP_EQUAL(BOOST_PP_SEQ_SIZE(params), 1), \ PROC_SINGLE_PARAM_, PROC_MULTI_PARAMS_)(params) #define NAME_A_PARAM_(r, data, i, elem) (param_##i) #define ARGS_DESC_TO_PARAMS(descs) \ BOOST_PP_SEQ_FOR_EACH_I(NAME_A_PARAM_, _, descs) #define IS_WITH_DEFAULT_VAL_(desc) \ BOOST_PP_EQUAL(BOOST_PP_SEQ_SIZE(desc), 2) #define SET_DEFAULT_VAL_(val) = BOOST_PP_SEQ_HEAD(val) #define DO_NOTHING(x) #define DECLARE_AN_ARG_COMMA_(r, names, i, desc) \ BOOST_PP_SEQ_HEAD(desc) BOOST_PP_SEQ_ELEM(i, names) \ BOOST_PP_IF(IS_WITH_DEFAULT_VAL_(desc), SET_DEFAULT_VAL_, DO_NOTHING) \ (BOOST_PP_SEQ_TAIL(desc)), #define DECLARE_AN_ARG_NO_COMMA_(name, desc) \ BOOST_PP_SEQ_HEAD(desc) name \ BOOST_PP_IF(IS_WITH_DEFAULT_VAL_(desc), SET_DEFAULT_VAL_, DO_NOTHING) \ (BOOST_PP_SEQ_TAIL(desc)) #define SINGLE_ARG_DESC_TO_DECLARATION_(desc) \ DECLARE_AN_ARG_NO_COMMA_( \ BOOST_PP_SEQ_ELEM(0, ARGS_DESC_TO_PARAMS(desc)), \ BOOST_PP_SEQ_ELEM(0, desc)) #define MULTI_ARGS_DESC_TO_DECLARATION_(descs) \ BOOST_PP_SEQ_FOR_EACH_I(DECLARE_AN_ARG_COMMA_, \ BOOST_PP_SEQ_SUBSEQ(ARGS_DESC_TO_PARAMS(descs), \ 0, BOOST_PP_DEC(BOOST_PP_SEQ_SIZE(descs))), \ BOOST_PP_SEQ_SUBSEQ(descs, 0, \ BOOST_PP_DEC(BOOST_PP_SEQ_SIZE(descs)))) \ DECLARE_AN_ARG_NO_COMMA_( \ BOOST_PP_SEQ_ELEM(BOOST_PP_DEC(BOOST_PP_SEQ_SIZE(descs)), \ ARGS_DESC_TO_PARAMS(descs)), \ BOOST_PP_SEQ_ELEM(BOOST_PP_DEC(BOOST_PP_SEQ_SIZE(descs)), descs)) #define ARGS_DESC_TO_DECLARATION(descs) \ BOOST_PP_IF(BOOST_PP_EQUAL(BOOST_PP_SEQ_SIZE(descs), 1), \ SINGLE_ARG_DESC_TO_DECLARATION_, \ MULTI_ARGS_DESC_TO_DECLARATION_)(descs) #define TO_VARIADIC_IMPL_COMMA_(r, _, elem) elem, #define TO_VARIADIC_IMPL_NO_COMMA_(elem) elem #define SEQ_TO_VARIADICS(seqs) \ BOOST_PP_SEQ_FOR_EACH(TO_VARIADIC_IMPL_COMMA_, _, \ BOOST_PP_SEQ_SUBSEQ(seqs, 0, BOOST_PP_DEC(BOOST_PP_SEQ_SIZE(seqs)))) \ TO_VARIADIC_IMPL_NO_COMMA_( \ BOOST_PP_SEQ_ELEM(BOOST_PP_DEC(BOOST_PP_SEQ_SIZE(seqs)), seqs)) #define DEF_MEMFN_SP_2_(ret_class, api_name) \ std::shared_ptr api_name() { \ std::string this_obj_name = TraceGetObjName(); \ std::string obj_name = \ Tracer::allocate_obj_name(Tracer::get_obj_prefix(#ret_class)); \ Tracer::logging_msg("auto %s = %s->%s();\n", obj_name.c_str(), \ this_obj_name.c_str(), __FUNCTION__); \ auto obj_impl_sp = impl_->api_name(); \ auto obj_sp = std::make_shared(obj_impl_sp); \ Tracer::insert_obj_name(static_cast(obj_impl_sp.get()), obj_name); \ return obj_sp; \ } #define DEF_MEMFN_SP_3_(ret_class, api_name, args_desc) \ std::shared_ptr api_name(ARGS_DESC_TO_DECLARATION(args_desc)) { \ std::string this_obj_name = TraceGetObjName(); \ std::string obj_name = \ Tracer::allocate_obj_name(Tracer::get_obj_prefix(#ret_class)); \ Tracer::logging_msg("auto %s = %s->%s(", obj_name.c_str(), \ this_obj_name.c_str(), __FUNCTION__); \ LOG_PARAMS(ARGS_DESC_TO_PARAMS(args_desc)) \ Tracer::dump_params_log_cache(); \ Tracer::logging_msg(");\n"); \ auto obj_impl_sp = impl_->api_name( \ PROC_PARAMS(ARGS_DESC_TO_PARAMS(args_desc))); \ auto obj_sp = std::make_shared(obj_impl_sp); \ Tracer::insert_obj_name(static_cast(obj_impl_sp.get()), obj_name); \ return obj_sp; \ } #define DEF_MEMFN_SP_4_(ret_class, api_name, args_desc, SPECIAL_MACRO_) \ std::shared_ptr api_name(ARGS_DESC_TO_DECLARATION(args_desc)) { \ std::string this_obj_name = TraceGetObjName(); \ std::string obj_name = \ Tracer::allocate_obj_name(Tracer::get_obj_prefix(#ret_class)); \ Tracer::push_back_msg_cache("auto " + obj_name + " = " + this_obj_name \ + "->" + __FUNCTION__ + "("); \ LOG_PARAMS(ARGS_DESC_TO_PARAMS(args_desc)) \ SPECIAL_MACRO_(ARGS_DESC_TO_PARAMS(args_desc)) \ Tracer::pop_params_log_cache(); \ Tracer::amend_last_msg_cache(");\n"); \ Tracer::msg_cache_sync_to_file(); \ auto obj_impl_sp = impl_->api_name( \ PROC_PARAMS(ARGS_DESC_TO_PARAMS(args_desc))); \ auto obj_sp = std::make_shared(obj_impl_sp); \ Tracer::insert_obj_name(static_cast(obj_impl_sp.get()), obj_name); \ return obj_sp; \ } #define DEF_SIMPLE_UNTRACED_API(retval, api_name) \ retval api_name() { \ return impl_->api_name(); \ } #define DEF_MEMFN_2_(retval, api_name) \ retval api_name() { \ std::string this_obj_name = TraceGetObjName(); \ Tracer::logging_msg("%s->%s();\n", \ this_obj_name.c_str(), __FUNCTION__); \ return impl_->api_name(); \ } #define DEF_MEMFN_3_(retval, api_name, args_desc) \ retval api_name(ARGS_DESC_TO_DECLARATION(args_desc)) { \ std::string this_obj_name = TraceGetObjName(); \ Tracer::logging_msg("%s->%s(", \ this_obj_name.c_str(), __FUNCTION__); \ LOG_PARAMS(ARGS_DESC_TO_PARAMS(args_desc)) \ Tracer::dump_params_log_cache(); \ Tracer::logging_msg(");\n"); \ return impl_->api_name( \ PROC_PARAMS(ARGS_DESC_TO_PARAMS(args_desc))); \ } #define DEF_MEMFN_4_(retval, api_name, args_desc, SPECIAL_MACRO_) \ retval api_name(ARGS_DESC_TO_DECLARATION(args_desc)) { \ std::string this_obj_name = TraceGetObjName(); \ Tracer::push_back_msg_cache( \ this_obj_name + "->" + __FUNCTION__ + "("); \ LOG_PARAMS(ARGS_DESC_TO_PARAMS(args_desc)) \ SPECIAL_MACRO_(ARGS_DESC_TO_PARAMS(args_desc)) \ Tracer::pop_params_log_cache(); \ Tracer::amend_last_msg_cache(");\n"); \ Tracer::msg_cache_sync_to_file(); \ return impl_->api_name( \ PROC_PARAMS(ARGS_DESC_TO_PARAMS(args_desc))); \ } #define DEF_INPLACE_MEMFN_2_(retval, api_name) \ retval api_name() { \ std::string this_obj_name = TraceGetObjName(); \ Tracer::logging_msg("%s->%s();\n", \ this_obj_name.c_str(), __FUNCTION__); \ impl_->api_name(); \ return *this; \ } #define DEF_INPLACE_MEMFN_3_(retval, api_name, args_desc) \ retval api_name(ARGS_DESC_TO_DECLARATION(args_desc)) { \ std::string this_obj_name = TraceGetObjName(); \ Tracer::logging_msg("%s->%s(", \ this_obj_name.c_str(), __FUNCTION__); \ LOG_PARAMS(ARGS_DESC_TO_PARAMS(args_desc)) \ Tracer::dump_params_log_cache(); \ Tracer::logging_msg(");\n"); \ impl_->api_name(PROC_PARAMS(ARGS_DESC_TO_PARAMS(args_desc))); \ return *this; \ } #define DEF_INPLACE_MEMFN_4_(retval, api_name, args_desc, SPECIAL_MACRO_) \ retval api_name(ARGS_DESC_TO_DECLARATION(args_desc)) { \ std::string this_obj_name = TraceGetObjName(); \ Tracer::push_back_msg_cache( \ this_obj_name + "->" + __FUNCTION__ + "("); \ LOG_PARAMS(ARGS_DESC_TO_PARAMS(args_desc)) \ SPECIAL_MACRO_(ARGS_DESC_TO_PARAMS(args_desc)) \ Tracer::pop_params_log_cache(); \ Tracer::amend_last_msg_cache(");\n"); \ Tracer::msg_cache_sync_to_file(); \ impl_->api_name(PROC_PARAMS(ARGS_DESC_TO_PARAMS(args_desc))); \ return *this; \ } #define DEF_CONSTRUCTOR_1_(class_name) \ class_name() { \ std::string obj_name = \ Tracer::allocate_obj_name(Tracer::get_obj_prefix(#class_name)); \ Tracer::logging_msg("auto %s = %s::%s();\n", obj_name.c_str(), \ target_namespace_name_, __FUNCTION__); \ impl_ = std::make_shared(); \ Tracer::insert_traced_obj( \ static_cast(impl_.get()), static_cast(this)); \ Tracer::insert_obj_name(static_cast(impl_.get()), obj_name); \ } #define DEF_CONSTRUCTOR_2_(class_name, args_desc) \ class_name(ARGS_DESC_TO_DECLARATION(args_desc)) { \ std::string obj_name = \ Tracer::allocate_obj_name(Tracer::get_obj_prefix(#class_name)); \ Tracer::logging_msg("auto %s = %s::%s(", obj_name.c_str(), \ target_namespace_name_, __FUNCTION__); \ LOG_PARAMS(ARGS_DESC_TO_PARAMS(args_desc)) \ Tracer::dump_params_log_cache(); \ Tracer::logging_msg(");\n"); \ impl_ = std::make_shared( \ PROC_PARAMS(ARGS_DESC_TO_PARAMS(args_desc))); \ Tracer::insert_traced_obj( \ static_cast(impl_.get()), static_cast(this)); \ Tracer::insert_obj_name(static_cast(impl_.get()), obj_name); \ } #define DEF_CONSTRUCTOR_3_(class_name, args_desc, SPECIAL_MACRO_) \ class_name(ARGS_DESC_TO_DECLARATION(args_desc)) { \ std::string obj_name = \ Tracer::allocate_obj_name(Tracer::get_obj_prefix(#class_name)); \ Tracer::push_back_msg_cache( \ "auto " + obj_name + " = " + target_namespace_name_ + "::" + \ __FUNCTION__ + "("); \ LOG_PARAMS(ARGS_DESC_TO_PARAMS(args_desc)) \ SPECIAL_MACRO_(ARGS_DESC_TO_PARAMS(args_desc)) \ Tracer::pop_params_log_cache(); \ Tracer::amend_last_msg_cache(");\n"); \ Tracer::msg_cache_sync_to_file(); \ impl_ = std::make_shared( \ PROC_PARAMS(ARGS_DESC_TO_PARAMS(args_desc))); \ Tracer::insert_traced_obj( \ static_cast(impl_.get()), static_cast(this)); \ Tracer::insert_obj_name(static_cast(impl_.get()), obj_name); \ } /* * Follows code to detect empty macro variadic is from: * https://gustedt.wordpress.com/2010/06/08/detect-empty-macro-arguments/ */ #define __ARG16(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13,_14,\ _15, ...) _15 #define __HAS_COMMA(...) __ARG16(__VA_ARGS__, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, \ 1, 1, 1, 0) #define __TRIGGER_PARENTHESIS_(...) , #define __PASTE5(_0, _1, _2, _3, _4) _0 ## _1 ## _2 ## _3 ## _4 #define __IS_EMPTY_CASE_0001 , #define __IS_EMPTY(_0, _1, _2, _3) __HAS_COMMA( \ __PASTE5(__IS_EMPTY_CASE_, _0, _1, _2, _3)) #define TUPLE_IS_EMPTY(...) \ __IS_EMPTY( \ /* test if there is just one argument, eventually an empty one */ \ __HAS_COMMA(__VA_ARGS__), \ /* test if _TRIGGER_PARENTHESIS_ together with the argument adds a comma */\ __HAS_COMMA(__TRIGGER_PARENTHESIS_ __VA_ARGS__), \ /* test if the argument together with a parenthesis adds a comma */ \ __HAS_COMMA(__VA_ARGS__ (/*empty*/)), \ /* test if placing it between _TRIGGER_PARENTHESIS_ */ \ /* and the parenthesis adds a comma */ \ __HAS_COMMA(__TRIGGER_PARENTHESIS_ __VA_ARGS__ (/*empty*/)) \ ) #define EMPTY_LAMBDA_ placeholder // BOOST_PP_VARIADIC_SIZE() variadic is empty, but it's will expand to 1. // And there are unknow issue of tensorflow build, ##__VA_ARGS__ can't correctly // remove the comma before. #define VARIADIC_SIZE_PLUS_ONE(...) \ BOOST_PP_VARIADIC_SIZE(EMPTY_LAMBDA_, ##__VA_ARGS__) #define INVOKE_LAMBDA_(r, _, lambda) lambda(); #define INVOKE_LAMBDAS_(...) \ BOOST_PP_IF(BOOST_PP_EQUAL(TUPLE_IS_EMPTY(__VA_ARGS__), 1), \ BOOST_PP_EMPTY(), BOOST_PP_SEQ_FOR_EACH(INVOKE_LAMBDA_, _, \ BOOST_PP_VARIADIC_TO_SEQ(__VA_ARGS__))) /* * In substitution for traced obj, api_impl got pointer retval because if * directly return reference will get build error of using pure virtual class * constructor. */ #define DEF_TRACED_API(Ret, api_name, ...) \ template \ typename std::enable_if_t::value, R> api_name( \ Args... params) { \ std::string this_obj_name = TraceGetObjName(); \ Tracer::push_back_msg_cache( \ this_obj_name + "->" + __FUNCTION__ + "("); \ Tracer::clear_params_log_cache(); \ boost::hana::tuple params_tuple = {params...}; \ boost::hana::for_each(params_tuple, [&] (auto x) { \ Tracer::logging_param(x, -1); \ }); \ INVOKE_LAMBDAS_(__VA_ARGS__) \ Tracer::pop_params_log_cache(); \ Tracer::amend_last_msg_cache(");\n"); \ Tracer::msg_cache_sync_to_file(); \ auto api_impl = [&] (auto&&... ts) { \ return &(impl_->api_name(ts...)); \ }; \ auto proc_param_lambda = [] (auto&& t) { \ return Tracer::proc_param(t); \ }; \ auto params_impl = boost::hana::transform(params_tuple, proc_param_lambda);\ auto ret_impl_p = boost::hana::unpack(params_impl, api_impl); \ return *static_cast*>(Tracer::get_traced_obj(ret_impl_p)); \ } \ \ template \ typename std::enable_if_t::value, R> api_name( \ Args... params) { \ std::string this_obj_name = TraceGetObjName(); \ std::string obj_name = \ Tracer::allocate_obj_name(Tracer::get_obj_prefix( \ boost::typeindex::type_id() \ .pretty_name())); \ Tracer::push_back_msg_cache( \ this_obj_name + "->" + __FUNCTION__ + "("); \ Tracer::clear_params_log_cache(); \ boost::hana::tuple params_tuple = {params...}; \ boost::hana::for_each(params_tuple, [&] (auto x) { \ Tracer::logging_param(x, -1); \ }); \ INVOKE_LAMBDAS_(__VA_ARGS__) \ Tracer::pop_params_log_cache(); \ Tracer::amend_last_msg_cache(");\n"); \ Tracer::msg_cache_sync_to_file(); \ auto api_impl = [&] (auto&&... ts) { \ return impl_->api_name(ts...); \ }; \ auto proc_param_lambda = [] (auto&& t) { \ return Tracer::proc_param(t); \ }; \ auto params_impl = boost::hana::transform(params_tuple, proc_param_lambda);\ auto obj_impl_sp = boost::hana::unpack(params_impl, api_impl); \ R obj_sp(obj_impl_sp); \ Tracer::insert_obj_name(static_cast(obj_impl_sp.get()), obj_name); \ return obj_sp; \ } \ \ template \ typename std::enable_if_t::value, R> api_name( \ Args... params) { \ std::string this_obj_name = TraceGetObjName(); \ Tracer::push_back_msg_cache( \ this_obj_name + "->" + __FUNCTION__ + "("); \ Tracer::clear_params_log_cache(); \ boost::hana::tuple params_tuple = {params...}; \ boost::hana::for_each(params_tuple, [&] (auto x) { \ Tracer::logging_param(x, -1); \ }); \ INVOKE_LAMBDAS_(__VA_ARGS__) \ Tracer::pop_params_log_cache(); \ Tracer::amend_last_msg_cache(");\n"); \ Tracer::msg_cache_sync_to_file(); \ auto api_impl = [&] (auto&&... ts) { \ return impl_->api_name(ts...); \ }; \ auto proc_param_lambda = [] (auto&& t) { \ return Tracer::proc_param(t); \ }; \ auto params_impl = boost::hana::transform(params_tuple, proc_param_lambda);\ auto ret_impl = boost::hana::unpack(params_impl, api_impl); \ R ret; \ for(auto& x : ret_impl) { \ ret.push_back(std::make_shared( \ *static_cast( \ Tracer::get_traced_obj(x.get())))); \ } \ return ret; \ } \ \ template \ typename std::enable_if_t::value, R> api_name( \ Args... params) { \ std::string this_obj_name = TraceGetObjName(); \ Tracer::push_back_msg_cache( \ this_obj_name + "->" + __FUNCTION__ + "("); \ Tracer::clear_params_log_cache(); \ boost::hana::tuple params_tuple = {params...}; \ boost::hana::for_each(params_tuple, [&] (auto x) { \ Tracer::logging_param(x, -1); \ }); \ INVOKE_LAMBDAS_(__VA_ARGS__) \ Tracer::pop_params_log_cache(); \ Tracer::amend_last_msg_cache(");\n"); \ Tracer::msg_cache_sync_to_file(); \ auto api_impl = [&] (auto&&... ts) { \ return impl_->api_name(ts...); \ }; \ auto proc_param_lambda = [] (auto&& t) { \ return Tracer::proc_param(t); \ }; \ auto params_impl = boost::hana::transform(params_tuple, proc_param_lambda);\ return boost::hana::unpack(params_impl, api_impl); \ } #define GET_MACRO_OVERLOAD_4_(_1, _2, _3, _4, MACRO, ...) MACRO #define GET_MACRO_OVERLOAD_3_(_1, _2, _3, MACRO, ...) MACRO #define DEF_MEMFN_SP(...) \ GET_MACRO_OVERLOAD_4_(__VA_ARGS__, \ DEF_MEMFN_SP_4_, \ DEF_MEMFN_SP_3_, \ DEF_MEMFN_SP_2_)(__VA_ARGS__) #define DEF_MEMFN(...) \ GET_MACRO_OVERLOAD_4_(__VA_ARGS__, \ DEF_MEMFN_4_, \ DEF_MEMFN_3_, \ DEF_MEMFN_2_)(__VA_ARGS__) #define DEF_INPLACE_MEMFN(...) \ GET_MACRO_OVERLOAD_4_(__VA_ARGS__, \ DEF_INPLACE_MEMFN_4_, \ DEF_INPLACE_MEMFN_3_, \ DEF_INPLACE_MEMFN_2_)(__VA_ARGS__) #define DEF_CONSTRUCTOR(...) \ GET_MACRO_OVERLOAD_3_(__VA_ARGS__, \ DEF_CONSTRUCTOR_3_, \ DEF_CONSTRUCTOR_2_, \ DEF_CONSTRUCTOR_1_)(__VA_ARGS__) #define LOGGING_POINTER_MSG(offset, length, idx) \ char log_msg[1024] = {0}; \ snprintf(log_msg, 1024, \ "trace::Replayer::get_vector(%u, %u).data()", \ offset, length); \ Tracer::insert_params_log_cache(std::string(log_msg), idx); #define DEF_INTERFACE_CONSTRUCTOR(interface) \ interface(const std::shared_ptr& impl) { \ Tracer::insert_traced_obj(static_cast(impl.get()), \ static_cast(this)); \ impl_ = impl; \ } #endif // TIM_EXPERIMENTAL_TRACE_TRACER_H_