Coverage Report

Created: 2025-04-24 12:01

/root/doris/be/src/util/runtime_profile.h
Line
Count
Source (jump to first uncovered line)
1
// Licensed to the Apache Software Foundation (ASF) under one
2
// or more contributor license agreements.  See the NOTICE file
3
// distributed with this work for additional information
4
// regarding copyright ownership.  The ASF licenses this file
5
// to you under the Apache License, Version 2.0 (the
6
// "License"); you may not use this file except in compliance
7
// with the License.  You may obtain a copy of the License at
8
//
9
//   http://www.apache.org/licenses/LICENSE-2.0
10
//
11
// Unless required by applicable law or agreed to in writing,
12
// software distributed under the License is distributed on an
13
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14
// KIND, either express or implied.  See the License for the
15
// specific language governing permissions and limitations
16
// under the License.
17
// This file is copied from
18
// https://github.com/apache/impala/blob/branch-2.9.0/be/src/util/runtime-profile.h
19
// and modified by Doris
20
21
#pragma once
22
23
#include <gen_cpp/Metrics_types.h>
24
#include <gen_cpp/RuntimeProfile_types.h>
25
#include <glog/logging.h>
26
#include <stdint.h>
27
28
#include <algorithm>
29
#include <atomic>
30
#include <cstdint>
31
#include <functional>
32
#include <iostream>
33
#include <map>
34
#include <memory>
35
#include <mutex>
36
#include <set>
37
#include <string>
38
#include <utility>
39
#include <vector>
40
41
#include "common/compiler_util.h" // IWYU pragma: keep
42
#include "common/logging.h"
43
#include "util/binary_cast.hpp"
44
#include "util/container_util.hpp"
45
#include "util/pretty_printer.h"
46
#include "util/stopwatch.hpp"
47
48
namespace doris {
49
class TRuntimeProfileNode;
50
class TRuntimeProfileTree;
51
class RuntimeProfileCounterTreeNode;
52
53
// Some macro magic to generate unique ids using __COUNTER__
54
2.09M
#define CONCAT_IMPL(x, y) x##y
55
2.09M
#define MACRO_CONCAT(x, y) CONCAT_IMPL(x, y)
56
57
#define ADD_LABEL_COUNTER(profile, name) (profile)->add_counter(name, TUnit::NONE)
58
#define ADD_LABEL_COUNTER_WITH_LEVEL(profile, name, level) \
59
    (profile)->add_counter_with_level(name, TUnit::NONE, level)
60
1.26M
#define ADD_COUNTER(profile, name, type) (profile)->add_counter(name, type)
61
#define ADD_COUNTER_WITH_LEVEL(profile, name, type, level) \
62
418k
    (profile)->add_counter_with_level(name, type, level)
63
628k
#define ADD_TIMER(profile, name) (profile)->add_counter(name, TUnit::TIME_NS)
64
#define ADD_TIMER_WITH_LEVEL(profile, name, level) \
65
549k
    (profile)->add_counter_with_level(name, TUnit::TIME_NS, level)
66
1
#define ADD_CHILD_COUNTER(profile, name, type, parent) (profile)->add_counter(name, type, parent)
67
#define ADD_CHILD_COUNTER_WITH_LEVEL(profile, name, type, parent, level) \
68
18
    (profile)->add_counter(name, type, parent, level)
69
683
#define ADD_CHILD_TIMER(profile, name, parent) (profile)->add_counter(name, TUnit::TIME_NS, parent)
70
#define ADD_CHILD_TIMER_WITH_LEVEL(profile, name, parent, level) \
71
23
    (profile)->add_counter(name, TUnit::TIME_NS, parent, level)
72
1.48M
#define SCOPED_TIMER(c) ScopedTimer<MonotonicStopWatch> MACRO_CONCAT(SCOPED_TIMER, __COUNTER__)(c)
73
#define SCOPED_TIMER_ATOMIC(c) \
74
    ScopedTimer<MonotonicStopWatch, std::atomic_bool> MACRO_CONCAT(SCOPED_TIMER, __COUNTER__)(c)
75
#define SCOPED_CPU_TIMER(c) \
76
27
    ScopedTimer<ThreadCpuStopWatch> MACRO_CONCAT(SCOPED_TIMER, __COUNTER__)(c)
77
#define CANCEL_SAFE_SCOPED_TIMER(c, is_cancelled) \
78
    ScopedTimer<MonotonicStopWatch> MACRO_CONCAT(SCOPED_TIMER, __COUNTER__)(c, is_cancelled)
79
#define SCOPED_RAW_TIMER(c)                                                                  \
80
612k
    doris::ScopedRawTimer<doris::MonotonicStopWatch, int64_t> MACRO_CONCAT(SCOPED_RAW_TIMER, \
81
612k
                                                                           __COUNTER__)(c)
82
#define SCOPED_ATOMIC_TIMER(c)                                                                 \
83
0
    ScopedRawTimer<MonotonicStopWatch, std::atomic<int64_t>> MACRO_CONCAT(SCOPED_ATOMIC_TIMER, \
84
0
                                                                          __COUNTER__)(c)
85
130k
#define COUNTER_UPDATE(c, v) (c)->update(v)
86
357k
#define COUNTER_SET(c, v) (c)->set(v)
87
88
class ObjectPool;
89
90
// Runtime profile is a group of profiling counters.  It supports adding named counters
91
// and being able to serialize and deserialize them.
92
// The profiles support a tree structure to form a hierarchy of counters.
93
// Runtime profiles supports measuring wall clock rate based counters.  There is a
94
// single thread per process that will convert an amount (i.e. bytes) counter to a
95
// corresponding rate based counter.  This thread wakes up at fixed intervals and updates
96
// all of the rate counters.
97
// Thread-safe.
98
class RuntimeProfile {
99
public:
100
    static std::unique_ptr<RuntimeProfile> from_thrift(const TRuntimeProfileTree& node);
101
102
    // The root counter name for all top level counters.
103
    static const std::string ROOT_COUNTER;
104
    class Counter {
105
    public:
106
        Counter(TUnit::type type, int64_t value = 0, int64_t level = 3)
107
4.22M
                : _value(value), _type(type), _level(level) {}
108
4.22M
        virtual ~Counter() = default;
109
110
4
        virtual Counter* clone() const { return new Counter(type(), value(), _level); }
111
112
1.98M
        virtual void update(int64_t delta) { _value.fetch_add(delta, std::memory_order_relaxed); }
113
114
0
        void bit_or(int64_t delta) { _value.fetch_or(delta, std::memory_order_relaxed); }
115
116
409k
        virtual void set(int64_t value) { _value.store(value, std::memory_order_relaxed); }
117
118
0
        virtual void set(double value) {
119
0
            DCHECK_EQ(sizeof(value), sizeof(int64_t));
120
0
            _value.store(binary_cast<double, int64_t>(value), std::memory_order_relaxed);
121
0
        }
122
123
379k
        virtual int64_t value() const { return _value.load(std::memory_order_relaxed); }
124
125
0
        virtual double double_value() const {
126
0
            return binary_cast<int64_t, double>(_value.load(std::memory_order_relaxed));
127
0
        }
128
129
39.5k
        virtual TCounter to_thrift(const std::string& name) const {
130
39.5k
            TCounter counter;
131
39.5k
            counter.name = name;
132
39.5k
            counter.value = this->value();
133
39.5k
            counter.type = this->type();
134
39.5k
            counter.__set_level(this->_level);
135
39.5k
            return counter;
136
39.5k
        }
137
138
        virtual void pretty_print(std::ostream* s, const std::string& prefix,
139
323
                                  const std::string& name) const {
140
323
            std::ostream& stream = *s;
141
323
            stream << prefix << "   - " << name << ": "
142
323
                   << PrettyPrinter::print(_value.load(std::memory_order_relaxed), type())
143
323
                   << std::endl;
144
323
        }
145
146
1.52M
        TUnit::type type() const { return _type; }
147
148
39.7k
        virtual int64_t level() const { return _level; }
149
150
3
        void set_level(int64_t level) { _level = level; }
151
152
        bool operator==(const Counter& other) const;
153
154
    private:
155
        friend class RuntimeProfile;
156
        friend class RuntimeProfileCounterTreeNode;
157
158
        std::atomic<int64_t> _value;
159
        TUnit::type _type;
160
        int64_t _level;
161
    };
162
163
    /// A counter that keeps track of the highest value seen (reporting that
164
    /// as value()) and the current value.
165
    class HighWaterMarkCounter : public Counter {
166
    public:
167
        HighWaterMarkCounter(TUnit::type unit, int64_t level, const std::string& parent_name,
168
                             int64_t value = 0, int64_t current_value = 0)
169
                : Counter(unit, value, level),
170
                  current_value_(current_value),
171
130k
                  _parent_name(parent_name) {}
172
173
0
        virtual Counter* clone() const override {
174
0
            return new HighWaterMarkCounter(type(), level(), parent_name(), value(),
175
0
                                            current_value());
176
0
        }
177
178
2.72k
        void add(int64_t delta) {
179
2.72k
            current_value_.fetch_add(delta, std::memory_order_relaxed);
180
2.72k
            if (delta > 0) {
181
1.35k
                UpdateMax(current_value_);
182
1.35k
            }
183
2.72k
        }
184
2.69k
        virtual void update(int64_t delta) override { add(delta); }
185
186
2
        TCounter to_thrift(const std::string& name) const override {
187
2
            TCounter counter;
188
2
            counter.name = std::move(name);
189
2
            counter.value = current_value();
190
2
            counter.type = type();
191
2
            counter.__set_level(level());
192
2
            return counter;
193
2
        }
194
195
2
        TCounter to_thrift_peak(std::string name) {
196
2
            TCounter counter;
197
2
            counter.name = std::move(name);
198
2
            counter.value = value();
199
2
            counter.type = type();
200
2
            counter.__set_level(level());
201
2
            return counter;
202
2
        }
203
204
        virtual void pretty_print(std::ostream* s, const std::string& prefix,
205
43
                                  const std::string& name) const override {
206
43
            std::ostream& stream = *s;
207
43
            stream << prefix << "   - " << name
208
43
                   << " Current: " << PrettyPrinter::print(current_value(), type()) << " (Peak: "
209
43
                   << PrettyPrinter::print(_value.load(std::memory_order_relaxed), type()) << ")"
210
43
                   << std::endl;
211
43
        }
212
213
        /// Tries to increase the current value by delta. If current_value() + delta
214
        /// exceeds max, return false and current_value is not changed.
215
0
        bool try_add(int64_t delta, int64_t max) {
216
0
            while (true) {
217
0
                int64_t old_val = current_value_.load(std::memory_order_relaxed);
218
0
                int64_t new_val = old_val + delta;
219
0
                if (UNLIKELY(new_val > max)) return false;
220
0
                if (LIKELY(current_value_.compare_exchange_weak(old_val, new_val,
221
0
                                                                std::memory_order_relaxed))) {
222
0
                    UpdateMax(new_val);
223
0
                    return true;
224
0
                }
225
0
            }
226
0
        }
227
228
104k
        void set(int64_t v) override {
229
104k
            current_value_.store(v, std::memory_order_relaxed);
230
104k
            UpdateMax(v);
231
104k
        }
232
233
45
        int64_t current_value() const { return current_value_.load(std::memory_order_relaxed); }
234
235
2
        std::string parent_name() const { return _parent_name; }
236
237
    private:
238
        /// Set '_value' to 'v' if 'v' is larger than '_value'. The entire operation is
239
        /// atomic.
240
105k
        void UpdateMax(int64_t v) {
241
105k
            while (true) {
242
105k
                int64_t old_max = _value.load(std::memory_order_relaxed);
243
105k
                int64_t new_max = std::max(old_max, v);
244
105k
                if (new_max == old_max) {
245
1.26k
                    break; // Avoid atomic update.
246
1.26k
                }
247
104k
                if (LIKELY(_value.compare_exchange_weak(old_max, new_max,
248
104k
                                                        std::memory_order_relaxed))) {
249
104k
                    break;
250
104k
                }
251
104k
            }
252
105k
        }
253
254
        /// The current value of the counter. _value in the super class represents
255
        /// the high water mark.
256
        std::atomic<int64_t> current_value_;
257
258
        const std::string _parent_name;
259
    };
260
261
    using DerivedCounterFunction = std::function<int64_t()>;
262
263
    // A DerivedCounter also has a name and type, but the value is computed.
264
    // Do not call Set() and Update().
265
    class DerivedCounter : public Counter {
266
    public:
267
        DerivedCounter(TUnit::type type, const DerivedCounterFunction& counter_fn,
268
                       int64_t value = 0, int64_t level = 1)
269
8
                : Counter(type, value, level), _counter_fn(counter_fn) {}
270
271
0
        virtual Counter* clone() const override {
272
0
            return new DerivedCounter(type(), _counter_fn, value(), level());
273
0
        }
274
275
3
        int64_t value() const override { return _counter_fn(); }
276
277
    private:
278
        DerivedCounterFunction _counter_fn;
279
    };
280
281
    using ConditionCounterFunction = std::function<bool(int64_t, int64_t)>;
282
283
    // ConditionCounter is a specialized counter that only updates its value when a specific condition is met.
284
    // It uses a condition function (condition_func) to determine when the counter's value should be updated.
285
    // This type of counter is particularly useful for tracking maximum values, minimum values, or other metrics
286
    // that should only be updated when they meet certain criteria.
287
    // For example, it can be used to record the maximum value of a specific metric during query execution,
288
    // or to update the counter only when a new value exceeds some threshold.
289
    class ConditionCounter : public Counter {
290
    public:
291
        ConditionCounter(TUnit::type type, const ConditionCounterFunction& condition_func,
292
                         int64_t level = 2, int64_t condition = 0, int64_t value = 0)
293
                : Counter(type, value, level),
294
                  _condition(condition),
295
                  _value(value),
296
2
                  _condition_func(condition_func) {}
297
298
0
        Counter* clone() const override {
299
0
            std::lock_guard<std::mutex> l(_mutex);
300
0
            return new ConditionCounter(type(), _condition_func, _condition, value(), level());
301
0
        }
302
303
7
        int64_t value() const override {
304
7
            std::lock_guard<std::mutex> l(_mutex);
305
7
            return _value;
306
7
        }
307
308
7
        void conditional_update(int64_t c, int64_t v) {
309
7
            std::lock_guard<std::mutex> l(_mutex);
310
7
            if (_condition_func(_condition, c)) {
311
4
                _value = v;
312
4
                _condition = c;
313
4
            }
314
7
        }
315
316
    private:
317
        mutable std::mutex _mutex;
318
        int64_t _condition;
319
        int64_t _value;
320
        ConditionCounterFunction _condition_func;
321
    };
322
323
    // NonZeroCounter will not be converted to Thrift if the value is 0.
324
    class NonZeroCounter : public Counter {
325
    public:
326
        NonZeroCounter(TUnit::type type, int64_t level, const std::string& parent_name,
327
                       int64_t value = 0)
328
34
                : Counter(type, value, level), _parent_name(parent_name) {}
329
330
0
        virtual Counter* clone() const override {
331
0
            return new NonZeroCounter(type(), level(), parent_name(), value());
332
0
        }
333
334
1
        std::string parent_name() const { return _parent_name; }
335
336
    private:
337
        const std::string _parent_name;
338
    };
339
340
    class DescriptionEntry : public Counter {
341
    public:
342
        DescriptionEntry(const std::string& name, const std::string& description)
343
78.0k
                : Counter(TUnit::NONE, 0, 2), _description(description), _name(name) {}
344
345
0
        virtual Counter* clone() const override {
346
0
            return new DescriptionEntry(_name, _description);
347
0
        }
348
349
0
        void set(int64_t value) override {
350
            // Do nothing
351
0
        }
352
0
        void set(double value) override {
353
            // Do nothing
354
0
        }
355
0
        void update(int64_t delta) override {
356
            // Do nothing
357
0
        }
358
359
1
        TCounter to_thrift(const std::string& name) const override {
360
1
            TCounter counter;
361
1
            counter.name = name;
362
1
            counter.__set_level(2);
363
1
            counter.__set_description(_description);
364
1
            return counter;
365
1
        }
366
367
    private:
368
        const std::string _description;
369
        const std::string _name;
370
    };
371
372
    // Create a runtime profile object with 'name'.
373
    RuntimeProfile(const std::string& name, bool is_averaged_profile = false);
374
375
    ~RuntimeProfile();
376
377
    // Adds a child profile.  This is thread safe.
378
    // 'indent' indicates whether the child will be printed w/ extra indentation
379
    // relative to the parent.
380
    // If location is non-null, child will be inserted after location.  Location must
381
    // already be added to the profile.
382
    void add_child(RuntimeProfile* child, bool indent, RuntimeProfile* location = nullptr);
383
384
    void add_child_unlock(RuntimeProfile* child, bool indent, RuntimeProfile* loc);
385
386
    /// Creates a new child profile with the given 'name'. A child profile with that name
387
    /// must not already exist. If 'prepend' is true, prepended before other child profiles,
388
    /// otherwise appended after other child profiles.
389
    RuntimeProfile* create_child(const std::string& name, bool indent = true, bool prepend = false);
390
391
    // Merges the src profile into this one, combining counters that have an identical
392
    // path. Info strings from profiles are not merged. 'src' would be a const if it
393
    // weren't for locking.
394
    // Calling this concurrently on two RuntimeProfiles in reverse order results in
395
    // undefined behavior.
396
    void merge(RuntimeProfile* src);
397
398
    // Updates this profile w/ the thrift profile: behaves like Merge(), except
399
    // that existing counters are updated rather than added up.
400
    // Info strings matched up by key and are updated or added, depending on whether
401
    // the key has already been registered.
402
    void update(const TRuntimeProfileTree& thrift_profile);
403
404
    // Add a counter with 'name'/'type'.  Returns a counter object that the caller can
405
    // update.  The counter is owned by the RuntimeProfile object.
406
    // If parent_counter_name is a non-empty string, the counter is added as a child of
407
    // parent_counter_name.
408
    // If the counter already exists, the existing counter object is returned.
409
    Counter* add_counter(const std::string& name, TUnit::type type,
410
                         const std::string& parent_counter_name, int64_t level = 2);
411
412
1.89M
    Counter* add_counter(const std::string& name, TUnit::type type) {
413
1.89M
        return add_counter(name, type, RuntimeProfile::ROOT_COUNTER);
414
1.89M
    }
415
416
1.04M
    Counter* add_counter_with_level(const std::string& name, TUnit::type type, int64_t level) {
417
1.04M
        return add_counter(name, type, RuntimeProfile::ROOT_COUNTER, level);
418
1.04M
    }
419
420
    NonZeroCounter* add_nonzero_counter(
421
            const std::string& name, TUnit::type type,
422
            const std::string& parent_counter_name = RuntimeProfile::ROOT_COUNTER,
423
            int64_t level = 2);
424
425
    // Add a description entry under target counter.
426
    void add_description(const std::string& name, const std::string& description,
427
                         std::string parent_counter_name);
428
    // Add a derived counter with 'name'/'type'. The counter is owned by the
429
    // RuntimeProfile object.
430
    // If parent_counter_name is a non-empty string, the counter is added as a child of
431
    // parent_counter_name.
432
    // Returns nullptr if the counter already exists.
433
    DerivedCounter* add_derived_counter(const std::string& name, TUnit::type type,
434
                                        const DerivedCounterFunction& counter_fn,
435
                                        const std::string& parent_counter_name);
436
437
    ConditionCounter* add_conditition_counter(const std::string& name, TUnit::type type,
438
                                              const ConditionCounterFunction& counter_fn,
439
                                              const std::string& parent_counter_name,
440
                                              int64_t level = 2);
441
442
    // Gets the counter object with 'name'.  Returns nullptr if there is no counter with
443
    // that name.
444
    Counter* get_counter(const std::string& name);
445
446
    // Adds all counters with 'name' that are registered either in this or
447
    // in any of the child profiles to 'counters'.
448
    void get_counters(const std::string& name, std::vector<Counter*>* counters);
449
450
    // Helper to append to the "ExecOption" info string.
451
0
    void append_exec_option(const std::string& option) { add_info_string("ExecOption", option); }
452
453
    // Adds a string to the runtime profile.  If a value already exists for 'key',
454
    // the value will be updated.
455
    void add_info_string(const std::string& key, const std::string& value);
456
457
    // Returns a pointer to the info string value for 'key'.  Returns nullptr if
458
    // the key does not exist.
459
    const std::string* get_info_string(const std::string& key);
460
461
    // Returns the counter for the total elapsed time.
462
73
    Counter* total_time_counter() { return &_counter_total_time; }
463
464
    // Prints the counters in a name: value format.
465
    // Does not hold locks when it makes any function calls.
466
    void pretty_print(std::ostream* s, const std::string& prefix = "",
467
                      int64_t profile_level = 2) const;
468
16
    std::string pretty_print() const {
469
16
        std::stringstream ss;
470
16
        pretty_print(&ss);
471
16
        return ss.str();
472
16
    };
473
474
    // Serializes profile to thrift.
475
    // Does not hold locks when it makes any function calls.
476
    void to_thrift(TRuntimeProfileTree* tree, int64_t profile_level = 2);
477
    void to_thrift(std::vector<TRuntimeProfileNode>* nodes, int64_t profile_level = 2);
478
479
    // Divides all counters by n
480
    void divide(int n);
481
482
    void get_children(std::vector<RuntimeProfile*>* children);
483
484
    // Gets all profiles in tree, including this one.
485
    void get_all_children(std::vector<RuntimeProfile*>* children);
486
487
    // Returns the number of counters in this profile
488
8
    int num_counters() const { return _counter_map.size(); }
489
490
    // Returns name of this profile
491
12
    const std::string& name() const { return _name; }
492
493
    // *only call this on top-level profiles*
494
    // (because it doesn't re-file child profiles)
495
0
    void set_name(const std::string& name) { _name = name; }
496
497
0
    int64_t metadata() const { return _metadata; }
498
104k
    void set_metadata(int64_t md) {
499
104k
        _is_set_metadata = true;
500
104k
        _metadata = md;
501
104k
    }
502
503
115
    bool is_set_metadata() const { return _is_set_metadata; }
504
505
0
    time_t timestamp() const { return _timestamp; }
506
0
    void set_timestamp(time_t ss) { _timestamp = ss; }
507
508
    // Derived counter function: return measured throughput as input_value/second.
509
    static int64_t units_per_second(const Counter* total_counter, const Counter* timer);
510
511
    // Derived counter function: return aggregated value
512
    static int64_t counter_sum(const std::vector<Counter*>* counters);
513
514
    /// Adds a high water mark counter to the runtime profile. Otherwise, same behavior
515
    /// as AddCounter().
516
    HighWaterMarkCounter* AddHighWaterMarkCounter(
517
            const std::string& name, TUnit::type unit,
518
            const std::string& parent_counter_name = RuntimeProfile::ROOT_COUNTER,
519
            int64_t profile_level = 2);
520
521
    // Recursively compute the fraction of the 'total_time' spent in this profile and
522
    // its children.
523
    // This function updates _local_time_percent for each profile.
524
    void compute_time_in_profile();
525
526
    void clear_children();
527
528
private:
529
    // RuntimeProfileCounterTreeNode needs to access the counter map and child counter map
530
    friend class RuntimeProfileCounterTreeNode;
531
    // Pool for allocated counters. Usually owned by the creator of this
532
    // object, but occasionally allocated in the constructor.
533
    std::unique_ptr<ObjectPool> _pool;
534
535
    // Pool for allocated counters. These counters are shared with some other objects.
536
    std::map<std::string, std::shared_ptr<HighWaterMarkCounter>> _shared_counter_pool;
537
538
    // Name for this runtime profile.
539
    std::string _name;
540
541
    // user-supplied, uninterpreted metadata.
542
    int64_t _metadata;
543
    bool _is_set_metadata = false;
544
545
    bool _is_sink = false;
546
    bool _is_set_sink = false;
547
548
    // The timestamp when the profile was modified, make sure the update is up to date.
549
    time_t _timestamp;
550
551
    /// True if this profile is an average derived from other profiles.
552
    /// All counters in this profile must be of unit AveragedCounter.
553
    bool _is_averaged_profile;
554
555
    // Map from counter names to counters.  The profile owns the memory for the
556
    // counters.
557
    using CounterMap = std::map<std::string, Counter*>;
558
    CounterMap _counter_map;
559
560
    // Map from parent counter name to a set of child counter name.
561
    // All top level counters are the child of RuntimeProfile::ROOT_COUNTER (root).
562
    using ChildCounterMap = std::map<std::string, std::set<std::string>>;
563
    ChildCounterMap _child_counter_map;
564
565
    // protects _counter_map, _counter_child_map and _bucketing_counters
566
    mutable std::mutex _counter_map_lock;
567
568
    // Child profiles.  Does not own memory.
569
    // We record children in both a map (to facilitate updates) and a vector
570
    // (to print things in the order they were registered)
571
    using ChildMap = std::map<std::string, RuntimeProfile*>;
572
    ChildMap _child_map;
573
    // vector of (profile, indentation flag)
574
    using ChildVector = std::vector<std::pair<RuntimeProfile*, bool>>;
575
    ChildVector _children;
576
    mutable std::mutex _children_lock; // protects _child_map and _children
577
578
    using InfoStrings = std::map<std::string, std::string>;
579
    InfoStrings _info_strings;
580
581
    // Keeps track of the order in which InfoStrings are displayed when printed
582
    using InfoStringsDisplayOrder = std::vector<std::string>;
583
    InfoStringsDisplayOrder _info_strings_display_order;
584
585
    // Protects _info_strings and _info_strings_display_order
586
    mutable std::mutex _info_strings_lock;
587
588
    Counter _counter_total_time;
589
    // Time spent in just in this profile (i.e. not the children) as a fraction
590
    // of the total time in the entire profile tree.
591
    double _local_time_percent;
592
593
    // update a subtree of profiles from nodes, rooted at *idx.
594
    // On return, *idx points to the node immediately following this subtree.
595
    void update(const std::vector<TRuntimeProfileNode>& nodes, int* idx);
596
597
    // Helper function to compute compute the fraction of the total time spent in
598
    // this profile and its children.
599
    // Called recursively.
600
    void compute_time_in_profile(int64_t total_time);
601
602
    // Print the child counters of the given counter name
603
    static void print_child_counters(const std::string& prefix, const std::string& counter_name,
604
                                     const CounterMap& counter_map,
605
                                     const ChildCounterMap& child_counter_map, std::ostream* s);
606
};
607
608
// Utility class to update the counter at object construction and destruction.
609
// When the object is constructed, decrement the counter by val.
610
// When the object goes out of scope, increment the counter by val.
611
class ScopedCounter {
612
public:
613
0
    ScopedCounter(RuntimeProfile::Counter* counter, int64_t val) : _val(val), _counter(counter) {
614
0
        if (counter == nullptr) {
615
0
            return;
616
0
        }
617
0
618
0
        _counter->update(-1L * _val);
619
0
    }
620
621
    // Increment the counter when object is destroyed
622
0
    ~ScopedCounter() {
623
0
        if (_counter != nullptr) {
624
0
            _counter->update(_val);
625
0
        }
626
0
    }
627
628
    // Disable copy constructor and assignment
629
    ScopedCounter(const ScopedCounter& counter) = delete;
630
    ScopedCounter& operator=(const ScopedCounter& counter) = delete;
631
632
private:
633
    int64_t _val;
634
    RuntimeProfile::Counter* _counter = nullptr;
635
};
636
637
// Utility class to update time elapsed when the object goes out of scope.
638
// 'T' must implement the stopWatch "interface" (start,stop,elapsed_time) but
639
// we use templates not to pay for virtual function overhead.
640
template <class T, typename Bool = bool>
641
class ScopedTimer {
642
public:
643
    ScopedTimer(RuntimeProfile::Counter* counter, const Bool* is_cancelled = nullptr)
644
1.48M
            : _counter(counter), _is_cancelled(is_cancelled) {
645
1.48M
        if (counter == nullptr) {
646
111
            return;
647
111
        }
648
1.48M
        DCHECK_EQ(counter->type(), TUnit::TIME_NS);
649
1.48M
        _sw.start();
650
1.48M
    }
_ZN5doris11ScopedTimerINS_15CustomStopWatchILi1EEEbEC2EPNS_14RuntimeProfile7CounterEPKb
Line
Count
Source
644
1.48M
            : _counter(counter), _is_cancelled(is_cancelled) {
645
1.48M
        if (counter == nullptr) {
646
111
            return;
647
111
        }
648
1.48M
        DCHECK_EQ(counter->type(), TUnit::TIME_NS);
649
1.48M
        _sw.start();
650
1.48M
    }
_ZN5doris11ScopedTimerINS_15CustomStopWatchILi3EEEbEC2EPNS_14RuntimeProfile7CounterEPKb
Line
Count
Source
644
27
            : _counter(counter), _is_cancelled(is_cancelled) {
645
27
        if (counter == nullptr) {
646
0
            return;
647
0
        }
648
27
        DCHECK_EQ(counter->type(), TUnit::TIME_NS);
649
27
        _sw.start();
650
27
    }
651
652
    void stop() { _sw.stop(); }
653
654
    void start() { _sw.start(); }
655
656
1.48M
    bool is_cancelled() { return _is_cancelled != nullptr && *_is_cancelled; }
_ZN5doris11ScopedTimerINS_15CustomStopWatchILi1EEEbE12is_cancelledEv
Line
Count
Source
656
1.48M
    bool is_cancelled() { return _is_cancelled != nullptr && *_is_cancelled; }
_ZN5doris11ScopedTimerINS_15CustomStopWatchILi3EEEbE12is_cancelledEv
Line
Count
Source
656
27
    bool is_cancelled() { return _is_cancelled != nullptr && *_is_cancelled; }
657
658
1.48M
    void UpdateCounter() {
659
1.48M
        if (_counter != nullptr && !is_cancelled()) {
660
1.48M
            _counter->update(_sw.elapsed_time());
661
1.48M
        }
662
1.48M
    }
_ZN5doris11ScopedTimerINS_15CustomStopWatchILi1EEEbE13UpdateCounterEv
Line
Count
Source
658
1.48M
    void UpdateCounter() {
659
1.48M
        if (_counter != nullptr && !is_cancelled()) {
660
1.48M
            _counter->update(_sw.elapsed_time());
661
1.48M
        }
662
1.48M
    }
_ZN5doris11ScopedTimerINS_15CustomStopWatchILi3EEEbE13UpdateCounterEv
Line
Count
Source
658
27
    void UpdateCounter() {
659
27
        if (_counter != nullptr && !is_cancelled()) {
660
27
            _counter->update(_sw.elapsed_time());
661
27
        }
662
27
    }
663
664
    // Update counter when object is destroyed
665
1.48M
    ~ScopedTimer() {
666
1.48M
        if (_counter == nullptr) {
667
111
            return;
668
111
        }
669
1.48M
        _sw.stop();
670
1.48M
        UpdateCounter();
671
1.48M
    }
_ZN5doris11ScopedTimerINS_15CustomStopWatchILi1EEEbED2Ev
Line
Count
Source
665
1.48M
    ~ScopedTimer() {
666
1.48M
        if (_counter == nullptr) {
667
111
            return;
668
111
        }
669
1.48M
        _sw.stop();
670
1.48M
        UpdateCounter();
671
1.48M
    }
_ZN5doris11ScopedTimerINS_15CustomStopWatchILi3EEEbED2Ev
Line
Count
Source
665
27
    ~ScopedTimer() {
666
27
        if (_counter == nullptr) {
667
0
            return;
668
0
        }
669
27
        _sw.stop();
670
27
        UpdateCounter();
671
27
    }
672
673
    // Disable copy constructor and assignment
674
    ScopedTimer(const ScopedTimer& timer) = delete;
675
    ScopedTimer& operator=(const ScopedTimer& timer) = delete;
676
677
private:
678
    T _sw;
679
    RuntimeProfile::Counter* _counter = nullptr;
680
    const Bool* _is_cancelled = nullptr;
681
};
682
683
// Utility class to update time elapsed when the object goes out of scope.
684
// 'T' must implement the stopWatch "interface" (start,stop,elapsed_time) but
685
// we use templates not to pay for virtual function overhead.
686
template <class T, class C>
687
class ScopedRawTimer {
688
public:
689
612k
    ScopedRawTimer(C* counter) : _counter(counter) { _sw.start(); }
_ZN5doris14ScopedRawTimerINS_15CustomStopWatchILi1EEElEC2EPl
Line
Count
Source
689
612k
    ScopedRawTimer(C* counter) : _counter(counter) { _sw.start(); }
Unexecuted instantiation: _ZN5doris14ScopedRawTimerINS_15CustomStopWatchILi1EEESt6atomicIlEEC2EPS4_
690
    // Update counter when object is destroyed
691
612k
    ~ScopedRawTimer() { *_counter += _sw.elapsed_time(); }
_ZN5doris14ScopedRawTimerINS_15CustomStopWatchILi1EEElED2Ev
Line
Count
Source
691
612k
    ~ScopedRawTimer() { *_counter += _sw.elapsed_time(); }
Unexecuted instantiation: _ZN5doris14ScopedRawTimerINS_15CustomStopWatchILi1EEESt6atomicIlEED2Ev
692
693
    // Disable copy constructor and assignment
694
    ScopedRawTimer(const ScopedRawTimer& timer) = delete;
695
    ScopedRawTimer& operator=(const ScopedRawTimer& timer) = delete;
696
697
private:
698
    T _sw;
699
    C* _counter = nullptr;
700
};
701
702
} // namespace doris