| /** |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| **/ |
| |
| #ifndef QUICKSTEP_UTILITY_EVENT_PROFILER_HPP_ |
| #define QUICKSTEP_UTILITY_EVENT_PROFILER_HPP_ |
| |
| #include <chrono> |
| #include <cstddef> |
| #include <cstring> |
| #include <ctime> |
| #include <iomanip> |
| #include <map> |
| #include <ostream> |
| #include <thread> |
| #include <type_traits> |
| #include <utility> |
| #include <vector> |
| |
| #include "threading/Mutex.hpp" |
| |
| #include "glog/logging.h" |
| |
| namespace quickstep { |
| |
| /** \addtogroup Utility |
| * @{ |
| */ |
| |
| using clock = std::chrono::steady_clock; |
| |
| template <typename TagT, typename ...PayloadT> |
| class EventProfiler { |
| |
| public: |
| EventProfiler() |
| : zero_time_(clock::now()) { |
| } |
| |
| struct EventInfo { |
| clock::time_point start_time; |
| clock::time_point end_time; |
| bool is_finished; |
| std::tuple<PayloadT...> payload; |
| |
| explicit EventInfo(const clock::time_point &start_time_in) |
| : start_time(start_time_in), |
| is_finished(false) { |
| } |
| |
| EventInfo() |
| : start_time(clock::now()), |
| is_finished(false) { |
| } |
| |
| inline void setPayload(PayloadT &&...in_payload) { |
| payload = std::make_tuple(in_payload...); |
| } |
| |
| inline void endEvent() { |
| end_time = clock::now(); |
| is_finished = true; |
| } |
| }; |
| |
| struct EventContainer { |
| EventContainer() |
| : context(0) {} |
| |
| inline void startEvent(const TagT &tag) { |
| events[tag].emplace_back(clock::now()); |
| } |
| |
| inline void endEvent(const TagT &tag) { |
| auto &event_info = events.at(tag).back(); |
| event_info.is_finished = true; |
| event_info.end_time = clock::now(); |
| } |
| |
| inline std::vector<EventInfo> *getEventLine(const TagT &tag) { |
| return &events[tag]; |
| } |
| |
| inline void setContext(int context_in) { |
| context = context_in; |
| } |
| |
| inline int getContext() const { |
| return context; |
| } |
| |
| std::map<TagT, std::vector<EventInfo>> events; |
| int context; |
| }; |
| |
| EventContainer *getContainer() { |
| MutexLock lock(mutex_); |
| return &thread_map_[std::this_thread::get_id()]; |
| } |
| |
| void writeToStream(std::ostream &os) const { |
| time_t rawtime; |
| time(&rawtime); |
| char event_id[32]; |
| strftime(event_id, sizeof event_id, "%Y-%m-%d %H:%M:%S", localtime(&rawtime)); |
| |
| int thread_id = 0; |
| for (const auto &thread_ctx : thread_map_) { |
| for (const auto &event_group : thread_ctx.second.events) { |
| for (const auto &event_info : event_group.second) { |
| CHECK(event_info.is_finished) |
| << "Unfinished profiling event at thread " << thread_id |
| << ": " << event_group.first; |
| |
| os << std::setprecision(12) |
| << event_id << "," |
| << thread_id << "," << event_group.first << ","; |
| |
| PrintTuple(os, event_info.payload, ","); |
| |
| os << std::chrono::duration<double>(event_info.start_time - zero_time_).count() |
| << "," |
| << std::chrono::duration<double>(event_info.end_time - zero_time_).count() |
| << "\n"; |
| } |
| } |
| ++thread_id; |
| } |
| } |
| |
| void clear() { |
| zero_time_ = clock::now(); |
| thread_map_.clear(); |
| } |
| |
| const std::map<std::thread::id, EventContainer> &containers() { |
| return thread_map_; |
| } |
| |
| const clock::time_point &zero_time() { |
| return zero_time_; |
| } |
| |
| private: |
| template<class Tuple, std::size_t N> |
| struct TuplePrinter { |
| static void Print(std::ostream &os, const Tuple &t, const std::string &sep) { |
| TuplePrinter<Tuple, N-1>::Print(os, t, sep); |
| os << std::get<N-1>(t) << sep; |
| } |
| }; |
| |
| template<class Tuple> |
| struct TuplePrinter<Tuple, 0> { |
| static void Print(std::ostream &os, const Tuple &t, const std::string &sep) { |
| } |
| }; |
| |
| template<class... Args> |
| static void PrintTuple(std::ostream &os, const std::tuple<Args...>& t, const std::string &sep) { |
| TuplePrinter<decltype(t), sizeof...(Args)>::Print(os, t, sep); |
| } |
| |
| clock::time_point zero_time_; |
| std::map<std::thread::id, EventContainer> thread_map_; |
| Mutex mutex_; |
| }; |
| |
| extern EventProfiler<std::string> simple_profiler; |
| |
| /** @} */ |
| |
| } // namespace quickstep |
| |
| #endif // QUICKSTEP_UTILITY_EVENT_PROFILER_HPP_ |