1cfca06d7SDimitry Andric //===-- Timer.cpp ---------------------------------------------------------===//
2f034231aSEd Maste //
35f29bb8aSDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
45f29bb8aSDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
55f29bb8aSDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6f034231aSEd Maste //
7f034231aSEd Maste //===----------------------------------------------------------------------===//
81b306c26SDimitry Andric #include "lldb/Utility/Timer.h"
974a628f7SDimitry Andric #include "lldb/Utility/Stream.h"
10b60736ecSDimitry Andric #include "llvm/Support/ManagedStatic.h"
11b60736ecSDimitry Andric #include "llvm/Support/Signposts.h"
1274a628f7SDimitry Andric
13f034231aSEd Maste #include <algorithm>
14f3fbd1c0SDimitry Andric #include <map>
15f3fbd1c0SDimitry Andric #include <mutex>
1694994d37SDimitry Andric #include <utility>
17f3fbd1c0SDimitry Andric #include <vector>
18f034231aSEd Maste
19344a3780SDimitry Andric #include <cassert>
20344a3780SDimitry Andric #include <cinttypes>
21344a3780SDimitry Andric #include <cstdarg>
22344a3780SDimitry Andric #include <cstdio>
23f034231aSEd Maste
24f034231aSEd Maste using namespace lldb_private;
25f034231aSEd Maste
26f034231aSEd Maste #define TIMER_INDENT_AMOUNT 2
27e81d9d49SDimitry Andric
2814f1b3e8SDimitry Andric namespace {
2914f1b3e8SDimitry Andric typedef std::vector<Timer *> TimerStack;
30b76161e4SDimitry Andric static std::atomic<Timer::Category *> g_categories;
31e81d9d49SDimitry Andric } // end of anonymous namespace
32e81d9d49SDimitry Andric
33b60736ecSDimitry Andric /// Allows llvm::Timer to emit signposts when supported.
34b60736ecSDimitry Andric static llvm::ManagedStatic<llvm::SignpostEmitter> Signposts;
35b60736ecSDimitry Andric
36e81d9d49SDimitry Andric std::atomic<bool> Timer::g_quiet(true);
37e81d9d49SDimitry Andric std::atomic<unsigned> Timer::g_display_depth(0);
GetFileMutex()3814f1b3e8SDimitry Andric static std::mutex &GetFileMutex() {
3914f1b3e8SDimitry Andric static std::mutex *g_file_mutex_ptr = new std::mutex();
40f3fbd1c0SDimitry Andric return *g_file_mutex_ptr;
41f3fbd1c0SDimitry Andric }
42e81d9d49SDimitry Andric
GetTimerStackForCurrentThread()43fdea456aSDimitry Andric static TimerStack &GetTimerStackForCurrentThread() {
44fdea456aSDimitry Andric static thread_local TimerStack g_stack;
45fdea456aSDimitry Andric return g_stack;
46f034231aSEd Maste }
47f034231aSEd Maste
Category(const char * cat)48b76161e4SDimitry Andric Timer::Category::Category(const char *cat) : m_name(cat) {
49b76161e4SDimitry Andric m_nanos.store(0, std::memory_order_release);
505f29bb8aSDimitry Andric m_nanos_total.store(0, std::memory_order_release);
515f29bb8aSDimitry Andric m_count.store(0, std::memory_order_release);
52b76161e4SDimitry Andric Category *expected = g_categories;
53b76161e4SDimitry Andric do {
54b76161e4SDimitry Andric m_next = expected;
55b76161e4SDimitry Andric } while (!g_categories.compare_exchange_weak(expected, this));
56b76161e4SDimitry Andric }
57b76161e4SDimitry Andric
SetQuiet(bool value)5814f1b3e8SDimitry Andric void Timer::SetQuiet(bool value) { g_quiet = value; }
59f034231aSEd Maste
Timer(Timer::Category & category,const char * format,...)60b76161e4SDimitry Andric Timer::Timer(Timer::Category &category, const char *format, ...)
6114f1b3e8SDimitry Andric : m_category(category), m_total_start(std::chrono::steady_clock::now()) {
62c0981da4SDimitry Andric Signposts->startInterval(this, m_category.GetName());
63fdea456aSDimitry Andric TimerStack &stack = GetTimerStackForCurrentThread();
64e81d9d49SDimitry Andric
65fdea456aSDimitry Andric stack.push_back(this);
666f8fc217SDimitry Andric if (!g_quiet && stack.size() <= g_display_depth) {
67f3fbd1c0SDimitry Andric std::lock_guard<std::mutex> lock(GetFileMutex());
68e81d9d49SDimitry Andric
69f034231aSEd Maste // Indent
70fdea456aSDimitry Andric ::fprintf(stdout, "%*s", int(stack.size() - 1) * TIMER_INDENT_AMOUNT, "");
71f034231aSEd Maste // Print formatted string
72f034231aSEd Maste va_list args;
73f034231aSEd Maste va_start(args, format);
74f3fbd1c0SDimitry Andric ::vfprintf(stdout, format, args);
75f034231aSEd Maste va_end(args);
76f034231aSEd Maste
77f034231aSEd Maste // Newline
78f3fbd1c0SDimitry Andric ::fprintf(stdout, "\n");
79f034231aSEd Maste }
80e81d9d49SDimitry Andric }
81f034231aSEd Maste
~Timer()8214f1b3e8SDimitry Andric Timer::~Timer() {
8314f1b3e8SDimitry Andric using namespace std::chrono;
8414f1b3e8SDimitry Andric
8514f1b3e8SDimitry Andric auto stop_time = steady_clock::now();
8614f1b3e8SDimitry Andric auto total_dur = stop_time - m_total_start;
8714f1b3e8SDimitry Andric auto timer_dur = total_dur - m_child_duration;
88f034231aSEd Maste
89c0981da4SDimitry Andric Signposts->endInterval(this, m_category.GetName());
90c0981da4SDimitry Andric
91fdea456aSDimitry Andric TimerStack &stack = GetTimerStackForCurrentThread();
926f8fc217SDimitry Andric if (!g_quiet && stack.size() <= g_display_depth) {
93f3fbd1c0SDimitry Andric std::lock_guard<std::mutex> lock(GetFileMutex());
9414f1b3e8SDimitry Andric ::fprintf(stdout, "%*s%.9f sec (%.9f sec)\n",
95fdea456aSDimitry Andric int(stack.size() - 1) * TIMER_INDENT_AMOUNT, "",
9614f1b3e8SDimitry Andric duration<double>(total_dur).count(),
9714f1b3e8SDimitry Andric duration<double>(timer_dur).count());
98f034231aSEd Maste }
99f034231aSEd Maste
100fdea456aSDimitry Andric assert(stack.back() == this);
101fdea456aSDimitry Andric stack.pop_back();
102fdea456aSDimitry Andric if (!stack.empty())
103fdea456aSDimitry Andric stack.back()->ChildDuration(total_dur);
10414f1b3e8SDimitry Andric
105f034231aSEd Maste // Keep total results for each category so we can dump results.
106b76161e4SDimitry Andric m_category.m_nanos += std::chrono::nanoseconds(timer_dur).count();
1075f29bb8aSDimitry Andric m_category.m_nanos_total += std::chrono::nanoseconds(total_dur).count();
1085f29bb8aSDimitry Andric m_category.m_count++;
109f034231aSEd Maste }
110f034231aSEd Maste
SetDisplayDepth(uint32_t depth)11114f1b3e8SDimitry Andric void Timer::SetDisplayDepth(uint32_t depth) { g_display_depth = depth; }
112f034231aSEd Maste
113f034231aSEd Maste /* binary function predicate:
114f034231aSEd Maste * - returns whether a person is less than another person
115f034231aSEd Maste */
1165f29bb8aSDimitry Andric namespace {
1175f29bb8aSDimitry Andric struct Stats {
1185f29bb8aSDimitry Andric const char *name;
1195f29bb8aSDimitry Andric uint64_t nanos;
1205f29bb8aSDimitry Andric uint64_t nanos_total;
1215f29bb8aSDimitry Andric uint64_t count;
1225f29bb8aSDimitry Andric };
1235f29bb8aSDimitry Andric } // namespace
124b76161e4SDimitry Andric
CategoryMapIteratorSortCriterion(const Stats & lhs,const Stats & rhs)1255f29bb8aSDimitry Andric static bool CategoryMapIteratorSortCriterion(const Stats &lhs,
1265f29bb8aSDimitry Andric const Stats &rhs) {
1275f29bb8aSDimitry Andric return lhs.nanos > rhs.nanos;
128f034231aSEd Maste }
129f034231aSEd Maste
ResetCategoryTimes()13014f1b3e8SDimitry Andric void Timer::ResetCategoryTimes() {
1315f29bb8aSDimitry Andric for (Category *i = g_categories; i; i = i->m_next) {
132b76161e4SDimitry Andric i->m_nanos.store(0, std::memory_order_release);
1335f29bb8aSDimitry Andric i->m_nanos_total.store(0, std::memory_order_release);
1345f29bb8aSDimitry Andric i->m_count.store(0, std::memory_order_release);
1355f29bb8aSDimitry Andric }
136f034231aSEd Maste }
137f034231aSEd Maste
DumpCategoryTimes(Stream & s)1387fa27ce4SDimitry Andric void Timer::DumpCategoryTimes(Stream &s) {
1395f29bb8aSDimitry Andric std::vector<Stats> sorted;
140b76161e4SDimitry Andric for (Category *i = g_categories; i; i = i->m_next) {
141b76161e4SDimitry Andric uint64_t nanos = i->m_nanos.load(std::memory_order_acquire);
1425f29bb8aSDimitry Andric if (nanos) {
1435f29bb8aSDimitry Andric uint64_t nanos_total = i->m_nanos_total.load(std::memory_order_acquire);
1445f29bb8aSDimitry Andric uint64_t count = i->m_count.load(std::memory_order_acquire);
1455f29bb8aSDimitry Andric Stats stats{i->m_name, nanos, nanos_total, count};
1465f29bb8aSDimitry Andric sorted.push_back(stats);
1475f29bb8aSDimitry Andric }
148f034231aSEd Maste }
149b76161e4SDimitry Andric if (sorted.empty())
150b76161e4SDimitry Andric return; // Later code will break without any elements.
151f034231aSEd Maste
152b76161e4SDimitry Andric // Sort by time
1534b4fe385SDimitry Andric llvm::sort(sorted, CategoryMapIteratorSortCriterion);
154b76161e4SDimitry Andric
1555f29bb8aSDimitry Andric for (const auto &stats : sorted)
1567fa27ce4SDimitry Andric s.Printf("%.9f sec (total: %.3fs; child: %.3fs; count: %" PRIu64
1575f29bb8aSDimitry Andric ") for %s\n",
1585f29bb8aSDimitry Andric stats.nanos / 1000000000., stats.nanos_total / 1000000000.,
1595f29bb8aSDimitry Andric (stats.nanos_total - stats.nanos) / 1000000000., stats.count,
1605f29bb8aSDimitry Andric stats.name);
161f034231aSEd Maste }
162