Coverage Report

Created: 2026-05-09 06:58

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
be/src/runtime/runtime_profile.h
Line
Count
Source
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 <gen_cpp/runtime_profile.pb.h>
26
#include <glog/logging.h>
27
#include <stdint.h>
28
29
#include <algorithm>
30
#include <atomic>
31
#include <cstdint>
32
#include <functional>
33
#include <iostream>
34
#include <map>
35
#include <memory>
36
#include <mutex>
37
#include <set>
38
#include <string>
39
#include <utility>
40
#include <vector>
41
42
#include "common/compiler_util.h" // IWYU pragma: keep
43
#include "common/logging.h"
44
#include "core/binary_cast.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
inline thread_local bool enable_profile_counter_check = true;
53
// Some macro magic to generate unique ids using __COUNTER__
54
242M
#define CONCAT_IMPL(x, y) x##y
55
242M
#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
52.9M
#define ADD_COUNTER(profile, name, type) (profile)->add_counter(name, type)
61
#define ADD_COUNTER_WITH_LEVEL(profile, name, type, level) \
62
18.1M
    (profile)->add_counter_with_level(name, type, level)
63
33.9M
#define ADD_TIMER(profile, name) (profile)->add_counter(name, TUnit::TIME_NS)
64
#define ADD_TIMER_WITH_LEVEL(profile, name, level) \
65
28.2M
    (profile)->add_counter_with_level(name, TUnit::TIME_NS, level)
66
2.02M
#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
13.7M
    (profile)->add_counter(name, type, parent, level)
69
20.6M
#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
14.3M
    (profile)->add_counter(name, TUnit::TIME_NS, parent, level)
72
92.3M
#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
13.2M
    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
136M
    doris::ScopedRawTimer<doris::MonotonicStopWatch, int64_t> MACRO_CONCAT(SCOPED_RAW_TIMER, \
81
136M
                                                                           __COUNTER__)(c)
82
#define SCOPED_ATOMIC_TIMER(c)                                                                 \
83
90.2k
    ScopedRawTimer<MonotonicStopWatch, std::atomic<int64_t>> MACRO_CONCAT(SCOPED_ATOMIC_TIMER, \
84
90.2k
                                                                          __COUNTER__)(c)
85
193M
#define COUNTER_UPDATE(c, v) (c)->update(v)
86
19.2M
#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
    static std::unique_ptr<RuntimeProfile> from_proto(const PRuntimeProfileTree& tree);
103
104
14
    static PProfileUnit unit_to_proto(const TUnit::type& type) {
105
14
        switch (type) {
106
11
        case TUnit::UNIT: {
107
11
            return PProfileUnit::UNIT;
108
0
        }
109
0
        case TUnit::UNIT_PER_SECOND: {
110
0
            return PProfileUnit::UNIT_PER_SECOND;
111
0
        }
112
0
        case TUnit::CPU_TICKS: {
113
0
            return PProfileUnit::CPU_TICKS;
114
0
        }
115
3
        case TUnit::BYTES: {
116
3
            return PProfileUnit::BYTES;
117
0
        }
118
0
        case TUnit::BYTES_PER_SECOND: {
119
0
            return PProfileUnit::BYTES_PER_SECOND;
120
0
        }
121
0
        case TUnit::TIME_NS: {
122
0
            return PProfileUnit::TIME_NS;
123
0
        }
124
0
        case TUnit::DOUBLE_VALUE: {
125
0
            return PProfileUnit::DOUBLE_VALUE;
126
0
        }
127
0
        case TUnit::NONE: {
128
0
            return PProfileUnit::NONE;
129
0
        }
130
0
        case TUnit::TIME_MS: {
131
0
            return PProfileUnit::TIME_MS;
132
0
        }
133
0
        case TUnit::TIME_S: {
134
0
            return PProfileUnit::TIME_S;
135
0
        }
136
0
        default: {
137
0
            DCHECK(false);
138
0
            return PProfileUnit::NONE;
139
0
        }
140
14
        }
141
14
    }
142
143
14
    static TUnit::type unit_to_thrift(const PProfileUnit& unit) {
144
14
        switch (unit) {
145
12
        case PProfileUnit::UNIT: {
146
12
            return TUnit::UNIT;
147
0
        }
148
0
        case PProfileUnit::UNIT_PER_SECOND: {
149
0
            return TUnit::UNIT_PER_SECOND;
150
0
        }
151
0
        case PProfileUnit::CPU_TICKS: {
152
0
            return TUnit::CPU_TICKS;
153
0
        }
154
2
        case PProfileUnit::BYTES: {
155
2
            return TUnit::BYTES;
156
0
        }
157
0
        case PProfileUnit::BYTES_PER_SECOND: {
158
0
            return TUnit::BYTES_PER_SECOND;
159
0
        }
160
0
        case PProfileUnit::TIME_NS: {
161
0
            return TUnit::TIME_NS;
162
0
        }
163
0
        case PProfileUnit::DOUBLE_VALUE: {
164
0
            return TUnit::DOUBLE_VALUE;
165
0
        }
166
0
        case PProfileUnit::NONE: {
167
0
            return TUnit::NONE;
168
0
        }
169
0
        case PProfileUnit::TIME_MS: {
170
0
            return TUnit::TIME_MS;
171
0
        }
172
0
        case PProfileUnit::TIME_S: {
173
0
            return TUnit::TIME_S;
174
0
        }
175
0
        default: {
176
0
            DCHECK(false);
177
0
            return TUnit::NONE;
178
0
        }
179
14
        }
180
14
    }
181
182
    // The root counter name for all top level counters.
183
    static const std::string ROOT_COUNTER;
184
    class Counter {
185
    public:
186
        Counter(TUnit::type type, int64_t value = 0, int64_t level = 3)
187
199M
                : _value(value), _type(type), _level(level) {}
188
205M
        virtual ~Counter() = default;
189
190
3.47k
        virtual Counter* clone() const { return new Counter(type(), value(), _level); }
191
192
305M
        virtual void update(int64_t delta) {
193
305M
#ifndef NDEBUG
194
305M
            int64_t prev_value = _value.load(std::memory_order_seq_cst);
195
            // Using memory_order_seq_cst to make sure no concurrency issues, it may affect
196
            // performance. So that only in debug mode, we check the counter value.
197
305M
            _value.fetch_add(delta, std::memory_order_seq_cst);
198
#else
199
            _value.fetch_add(delta, std::memory_order_relaxed);
200
#endif
201
305M
#ifndef NDEBUG
202
305M
            (void)prev_value;
203
305M
#if !defined(BE_TEST)
204
314M
            if (enable_profile_counter_check) {
205
314M
                if (delta < 0) {
206
0
                    DCHECK_GT(_value.load(std::memory_order_seq_cst), -1L)
207
0
                            << " delta: " << delta << " prev_value: " << prev_value;
208
0
                }
209
314M
            }
210
305M
#endif
211
305M
#endif
212
305M
        }
213
214
0
        void bit_or(int64_t delta) { _value.fetch_or(delta, std::memory_order_relaxed); }
215
216
15.2M
        virtual void set(int64_t value) {
217
15.2M
#ifndef NDEBUG
218
15.2M
            int64_t prev_value = _value.load(std::memory_order_seq_cst);
219
15.2M
            _value.store(value, std::memory_order_seq_cst);
220
#else
221
            _value.store(value, std::memory_order_relaxed);
222
#endif
223
15.2M
#ifndef NDEBUG
224
15.2M
            (void)prev_value;
225
15.2M
#if !defined(BE_TEST)
226
15.3M
            if (enable_profile_counter_check) {
227
15.3M
                DCHECK_GT(_value.load(std::memory_order_seq_cst), -1L)
228
0
                        << " new value: " << value << " prev_value: " << prev_value;
229
15.3M
            }
230
15.2M
#endif
231
15.2M
#endif
232
15.2M
        }
233
234
0
        virtual void set(double value) {
235
0
            DCHECK_EQ(sizeof(value), sizeof(int64_t));
236
0
#ifndef NDEBUG
237
0
            int64_t prev_value = _value.load(std::memory_order_seq_cst);
238
0
            _value.store(binary_cast<double, int64_t>(value), std::memory_order_seq_cst);
239
#else
240
            _value.store(binary_cast<double, int64_t>(value), std::memory_order_relaxed);
241
#endif
242
0
#ifndef NDEBUG
243
0
            (void)prev_value;
244
0
#if !defined(BE_TEST)
245
0
            if (enable_profile_counter_check) {
246
0
                DCHECK_GT(_value.load(std::memory_order_seq_cst), -1L)
247
0
                        << " new value: " << value << " prev_value: " << prev_value;
248
0
            }
249
0
#endif
250
0
#endif
251
0
        }
252
253
10.2M
        virtual int64_t value() const { return _value.load(std::memory_order_relaxed); }
254
255
0
        virtual double double_value() const {
256
0
            return binary_cast<int64_t, double>(_value.load(std::memory_order_relaxed));
257
0
        }
258
259
431k
        virtual TCounter to_thrift(const std::string& name) const {
260
431k
            TCounter counter;
261
431k
            counter.name = name;
262
431k
            counter.value = this->value();
263
431k
            counter.type = this->type();
264
431k
            counter.__set_level(this->_level);
265
431k
            return counter;
266
431k
        }
267
268
8
        virtual PProfileCounter to_proto(const std::string& name) const {
269
8
            PProfileCounter counter;
270
8
            counter.set_name(name);
271
8
            counter.set_value(this->value());
272
8
            counter.set_type(unit_to_proto(this->type()));
273
8
            counter.set_level(this->value());
274
8
            return counter;
275
8
        }
276
277
        virtual void pretty_print(std::ostream* s, const std::string& prefix,
278
350
                                  const std::string& name) const {
279
350
            std::ostream& stream = *s;
280
350
            stream << prefix << "   - " << name << ": "
281
350
                   << PrettyPrinter::print(_value.load(std::memory_order_relaxed), type())
282
350
                   << std::endl;
283
350
        }
284
285
104M
        TUnit::type type() const { return _type; }
286
287
1.37M
        virtual int64_t level() const { return _level; }
288
289
        void set_level(int64_t level) { _level = level; }
290
291
        bool operator==(const Counter& other) const;
292
293
    private:
294
        friend class RuntimeProfile;
295
        friend class RuntimeProfileCounterTreeNode;
296
297
        std::atomic<int64_t> _value;
298
        TUnit::type _type;
299
        int64_t _level;
300
    };
301
302
    /// A counter that keeps track of the highest value seen (reporting that
303
    /// as value()) and the current value.
304
    class HighWaterMarkCounter : public Counter {
305
    public:
306
        HighWaterMarkCounter(TUnit::type unit, int64_t level, const std::string& parent_name,
307
                             int64_t value = 0, int64_t current_value = 0)
308
7.37M
                : Counter(unit, value, level),
309
7.37M
                  current_value_(current_value),
310
7.37M
                  _parent_name(parent_name) {}
311
312
8.26k
        virtual Counter* clone() const override {
313
8.26k
            return new HighWaterMarkCounter(type(), level(), parent_name(), value(),
314
8.26k
                                            current_value());
315
8.26k
        }
316
317
6.15M
        void add(int64_t delta) {
318
6.15M
#ifndef NDEBUG
319
6.15M
            current_value_.fetch_add(delta, std::memory_order_seq_cst);
320
6.15M
            if (delta > 0) {
321
1.55M
                UpdateMax(current_value_);
322
1.55M
            }
323
            //if (enable_profile_counter_check) {
324
            //    DCHECK_GT(current_value_.load(std::memory_order_seq_cst), -1L);
325
            //}
326
#else
327
            current_value_.fetch_add(delta, std::memory_order_relaxed);
328
            if (delta > 0) {
329
                UpdateMax(current_value_);
330
            }
331
#endif
332
6.15M
        }
333
4.28M
        virtual void update(int64_t delta) override { add(delta); }
334
335
38.6k
        TCounter to_thrift(const std::string& name) const override {
336
38.6k
            TCounter counter;
337
38.6k
            counter.name = std::move(name);
338
38.6k
            counter.value = current_value();
339
38.6k
            counter.type = type();
340
38.6k
            counter.__set_level(level());
341
38.6k
            return counter;
342
38.6k
        }
343
344
0
        PProfileCounter to_proto(const std::string& name) const override {
345
0
            PProfileCounter counter;
346
0
            counter.set_name(name);
347
0
            counter.set_value(current_value());
348
0
            counter.set_type(unit_to_proto(this->type()));
349
0
            counter.set_level(this->value());
350
0
            return counter;
351
0
        }
352
353
38.6k
        TCounter to_thrift_peak(std::string name) {
354
38.6k
            TCounter counter;
355
38.6k
            counter.name = std::move(name);
356
38.6k
            counter.value = value();
357
38.6k
            counter.type = type();
358
38.6k
            counter.__set_level(level());
359
38.6k
            return counter;
360
38.6k
        }
361
362
0
        PProfileCounter to_proto_peak(const std::string& name) const {
363
0
            return Counter::to_proto(name);
364
0
        }
365
366
        virtual void pretty_print(std::ostream* s, const std::string& prefix,
367
82
                                  const std::string& name) const override {
368
82
            std::ostream& stream = *s;
369
82
            stream << prefix << "   - " << name
370
82
                   << " Current: " << PrettyPrinter::print(current_value(), type()) << " (Peak: "
371
82
                   << PrettyPrinter::print(_value.load(std::memory_order_relaxed), type()) << ")"
372
82
                   << std::endl;
373
82
        }
374
375
        /// Tries to increase the current value by delta. If current_value() + delta
376
        /// exceeds max, return false and current_value is not changed.
377
0
        bool try_add(int64_t delta, int64_t max) {
378
0
            while (true) {
379
0
                int64_t old_val = current_value_.load(std::memory_order_relaxed);
380
0
                int64_t new_val = old_val + delta;
381
0
                if (UNLIKELY(new_val > max)) return false;
382
0
                if (LIKELY(current_value_.compare_exchange_weak(old_val, new_val,
383
0
                                                                std::memory_order_relaxed))) {
384
0
                    UpdateMax(new_val);
385
0
                    return true;
386
0
                }
387
0
            }
388
0
        }
389
390
7.97M
        void set(int64_t v) override {
391
7.97M
#ifndef NDEBUG
392
7.97M
            int64_t prev_value = current_value_.load(std::memory_order_seq_cst);
393
7.97M
            int64_t prev_max_value = _value.load(std::memory_order_seq_cst);
394
7.97M
            current_value_.store(v, std::memory_order_seq_cst);
395
#else
396
            current_value_.store(v, std::memory_order_relaxed);
397
#endif
398
7.97M
            UpdateMax(v);
399
7.97M
#ifndef NDEBUG
400
7.97M
            (void)prev_value;
401
7.97M
            (void)prev_max_value;
402
7.97M
#if !defined(BE_TEST)
403
404
7.97M
            if (enable_profile_counter_check) {
405
1.90M
                DCHECK_GT(current_value_.load(std::memory_order_seq_cst), -1L)
406
0
                        << " prev_value: " << prev_value;
407
1.90M
                DCHECK_GT(_value.load(std::memory_order_seq_cst), -1L)
408
0
                        << " prev_max_value: " << prev_max_value << " prev_value: " << prev_value;
409
1.90M
            }
410
7.97M
#endif
411
7.97M
#endif
412
7.97M
        }
413
414
48.0k
        int64_t current_value() const { return current_value_.load(std::memory_order_relaxed); }
415
416
46.9k
        std::string parent_name() const { return _parent_name; }
417
418
    private:
419
        /// Set '_value' to 'v' if 'v' is larger than '_value'. The entire operation is
420
        /// atomic.
421
9.62M
        void UpdateMax(int64_t v) {
422
9.62M
            while (true) {
423
9.62M
                int64_t old_max = _value.load(std::memory_order_relaxed);
424
9.62M
                int64_t new_max = std::max(old_max, v);
425
9.62M
                if (new_max == old_max) {
426
8.35M
                    break; // Avoid atomic update.
427
8.35M
                }
428
1.26M
                if (LIKELY(_value.compare_exchange_weak(old_max, new_max,
429
1.26M
                                                        std::memory_order_relaxed))) {
430
1.26M
                    break;
431
1.26M
                }
432
1.26M
            }
433
9.62M
        }
434
435
        /// The current value of the counter. _value in the super class represents
436
        /// the high water mark.
437
        std::atomic<int64_t> current_value_;
438
439
        const std::string _parent_name;
440
    };
441
442
    using DerivedCounterFunction = std::function<int64_t()>;
443
444
    // A DerivedCounter also has a name and type, but the value is computed.
445
    // Do not call Set() and Update().
446
    class DerivedCounter : public Counter {
447
    public:
448
        DerivedCounter(TUnit::type type, const DerivedCounterFunction& counter_fn,
449
                       int64_t value = 0, int64_t level = 1)
450
563k
                : Counter(type, value, level), _counter_fn(counter_fn) {}
451
452
0
        virtual Counter* clone() const override {
453
0
            return new DerivedCounter(type(), _counter_fn, value(), level());
454
0
        }
455
456
8.03k
        int64_t value() const override { return _counter_fn(); }
457
458
    private:
459
        DerivedCounterFunction _counter_fn;
460
    };
461
462
    using ConditionCounterFunction = std::function<bool(int64_t, int64_t)>;
463
464
    // ConditionCounter is a specialized counter that only updates its value when a specific condition is met.
465
    // It uses a condition function (condition_func) to determine when the counter's value should be updated.
466
    // This type of counter is particularly useful for tracking maximum values, minimum values, or other metrics
467
    // that should only be updated when they meet certain criteria.
468
    // For example, it can be used to record the maximum value of a specific metric during query execution,
469
    // or to update the counter only when a new value exceeds some threshold.
470
    class ConditionCounter : public Counter {
471
    public:
472
        ConditionCounter(TUnit::type type, const ConditionCounterFunction& condition_func,
473
                         int64_t level = 2, int64_t condition = 0, int64_t value = 0)
474
4
                : Counter(type, value, level),
475
4
                  _condition(condition),
476
4
                  _stored_value(value),
477
4
                  _condition_func(condition_func) {}
478
479
0
        Counter* clone() const override {
480
0
            std::lock_guard<std::mutex> l(_mutex);
481
0
            return new ConditionCounter(type(), _condition_func, _condition, value(), level());
482
0
        }
483
484
7
        int64_t value() const override {
485
7
            std::lock_guard<std::mutex> l(_mutex);
486
7
            return _stored_value;
487
7
        }
488
489
9
        void conditional_update(int64_t c, int64_t v) {
490
9
            std::lock_guard<std::mutex> l(_mutex);
491
9
            if (_condition_func(_condition, c)) {
492
6
                _stored_value = v;
493
6
                _condition = c;
494
6
            }
495
9
        }
496
497
    private:
498
        mutable std::mutex _mutex;
499
        int64_t _condition;
500
        int64_t _stored_value;
501
        ConditionCounterFunction _condition_func;
502
    };
503
504
    // NonZeroCounter will not be converted to Thrift if the value is 0.
505
    class NonZeroCounter : public Counter {
506
    public:
507
        NonZeroCounter(TUnit::type type, int64_t level, const std::string& parent_name,
508
                       int64_t value = 0)
509
2.57M
                : Counter(type, value, level), _parent_name(parent_name) {}
510
511
0
        virtual Counter* clone() const override {
512
0
            return new NonZeroCounter(type(), level(), parent_name(), value());
513
0
        }
514
515
12.9k
        std::string parent_name() const { return _parent_name; }
516
517
    private:
518
        const std::string _parent_name;
519
    };
520
521
    class DescriptionEntry : public Counter {
522
    public:
523
        DescriptionEntry(const std::string& name, const std::string& description)
524
197k
                : Counter(TUnit::NONE, 0, 2), _description(description), _name(name) {}
525
526
0
        virtual Counter* clone() const override {
527
0
            return new DescriptionEntry(_name, _description);
528
0
        }
529
530
0
        void set(int64_t value) override {
531
            // Do nothing
532
0
        }
533
0
        void set(double value) override {
534
            // Do nothing
535
0
        }
536
0
        void update(int64_t delta) override {
537
            // Do nothing
538
0
        }
539
540
1
        TCounter to_thrift(const std::string& name) const override {
541
1
            TCounter counter;
542
1
            counter.name = name;
543
1
            counter.__set_level(2);
544
1
            counter.__set_description(_description);
545
1
            return counter;
546
1
        }
547
548
0
        PProfileCounter to_proto(const std::string& name) const override {
549
0
            PProfileCounter counter;
550
0
            counter.set_name(name);
551
0
            counter.set_level(2);
552
0
            counter.set_description(_description);
553
0
            return counter;
554
0
        }
555
556
    private:
557
        const std::string _description;
558
        const std::string _name;
559
    };
560
561
    // Create a runtime profile object with 'name'.
562
    RuntimeProfile(const std::string& name, bool is_averaged_profile = false);
563
564
    ~RuntimeProfile();
565
566
    // Adds a child profile.  This is thread safe.
567
    // 'indent' indicates whether the child will be printed w/ extra indentation
568
    // relative to the parent.
569
    // If location is non-null, child will be inserted after location.  Location must
570
    // already be added to the profile.
571
    void add_child(RuntimeProfile* child, bool indent, RuntimeProfile* location = nullptr);
572
573
    void add_child_unlock(RuntimeProfile* child, bool indent, RuntimeProfile* loc);
574
575
    /// Creates a new child profile with the given 'name'. A child profile with that name
576
    /// must not already exist. If 'prepend' is true, prepended before other child profiles,
577
    /// otherwise appended after other child profiles.
578
    RuntimeProfile* create_child(const std::string& name, bool indent = true, bool prepend = false);
579
580
    // Merges the src profile into this one, combining counters that have an identical
581
    // path. Info strings from profiles are not merged. 'src' would be a const if it
582
    // weren't for locking.
583
    // Calling this concurrently on two RuntimeProfiles in reverse order results in
584
    // undefined behavior.
585
    void merge(const RuntimeProfile* src);
586
587
    // Updates this profile w/ the thrift profile: behaves like Merge(), except
588
    // that existing counters are updated rather than added up.
589
    // Info strings matched up by key and are updated or added, depending on whether
590
    // the key has already been registered.
591
    void update(const TRuntimeProfileTree& thrift_profile);
592
593
    //Similar to `void update(const TRuntimeProfileTree& thrift_profile)`
594
    void update(const PRuntimeProfileTree& proto_profile);
595
596
    // Add a counter with 'name'/'type'.  Returns a counter object that the caller can
597
    // update.  The counter is owned by the RuntimeProfile object.
598
    // If parent_counter_name is a non-empty string, the counter is added as a child of
599
    // parent_counter_name.
600
    // If the counter already exists, the existing counter object is returned.
601
    Counter* add_counter(const std::string& name, TUnit::type type,
602
                         const std::string& parent_counter_name, int64_t level = 2);
603
604
85.4M
    Counter* add_counter(const std::string& name, TUnit::type type) {
605
85.4M
        return add_counter(name, type, RuntimeProfile::ROOT_COUNTER);
606
85.4M
    }
607
608
45.6M
    Counter* add_counter_with_level(const std::string& name, TUnit::type type, int64_t level) {
609
45.6M
        return add_counter(name, type, RuntimeProfile::ROOT_COUNTER, level);
610
45.6M
    }
611
612
    NonZeroCounter* add_nonzero_counter(
613
            const std::string& name, TUnit::type type,
614
            const std::string& parent_counter_name = RuntimeProfile::ROOT_COUNTER,
615
            int64_t level = 2);
616
617
    // Add a description entry under target counter.
618
    void add_description(const std::string& name, const std::string& description,
619
                         std::string parent_counter_name);
620
    // Add a derived counter with 'name'/'type'. The counter is owned by the
621
    // RuntimeProfile object.
622
    // If parent_counter_name is a non-empty string, the counter is added as a child of
623
    // parent_counter_name.
624
    // Returns nullptr if the counter already exists.
625
    DerivedCounter* add_derived_counter(const std::string& name, TUnit::type type,
626
                                        const DerivedCounterFunction& counter_fn,
627
                                        const std::string& parent_counter_name);
628
629
    ConditionCounter* add_conditition_counter(const std::string& name, TUnit::type type,
630
                                              const ConditionCounterFunction& counter_fn,
631
                                              const std::string& parent_counter_name,
632
                                              int64_t level = 2);
633
634
    // Gets the counter object with 'name'.  Returns nullptr if there is no counter with
635
    // that name.
636
    Counter* get_counter(const std::string& name);
637
638
    // Adds all counters with 'name' that are registered either in this or
639
    // in any of the child profiles to 'counters'.
640
    void get_counters(const std::string& name, std::vector<Counter*>* counters);
641
642
    // Helper to append to the "ExecOption" info string.
643
0
    void append_exec_option(const std::string& option) { add_info_string("ExecOption", option); }
644
645
    // Adds a string to the runtime profile.  If a value already exists for 'key',
646
    // the value will be updated.
647
    void add_info_string(const std::string& key, const std::string& value);
648
649
    // Returns a pointer to the info string value for 'key'.  Returns nullptr if
650
    // the key does not exist.
651
    const std::string* get_info_string(const std::string& key);
652
653
    // Returns the counter for the total elapsed time.
654
22.8M
    Counter* total_time_counter() { return &_counter_total_time; }
655
656
    // Prints the counters in a name: value format.
657
    // Does not hold locks when it makes any function calls.
658
    void pretty_print(std::ostream* s, const std::string& prefix = "",
659
                      int64_t profile_level = 2) const;
660
26
    std::string pretty_print() const {
661
26
        std::stringstream ss;
662
26
        pretty_print(&ss);
663
26
        return ss.str();
664
26
    };
665
666
    // Serializes profile to thrift.
667
    // Does not hold locks when it makes any function calls.
668
    void to_thrift(TRuntimeProfileTree* tree, int64_t profile_level = 2);
669
    void to_thrift(std::vector<TRuntimeProfileNode>* nodes, int64_t profile_level = 2);
670
671
    // Similar to `to_thrift`.
672
    void to_proto(PRuntimeProfileTree* tree, int64_t profile_level = 2);
673
    void to_proto(google::protobuf::RepeatedPtrField<PRuntimeProfileNode>* nodes,
674
                  int64_t profile_level = 2);
675
676
    // Divides all counters by n
677
    void divide(int n);
678
679
    RuntimeProfile* get_child(std::string name);
680
681
    void get_children(std::vector<RuntimeProfile*>* children) const;
682
683
    // Gets all profiles in tree, including this one.
684
    void get_all_children(std::vector<RuntimeProfile*>* children);
685
686
    // Returns the number of counters in this profile
687
    int num_counters() const { return cast_set<int>(_counter_map.size()); }
688
689
    // Returns name of this profile
690
5.20k
    const std::string& name() const { return _name; }
691
692
    // *only call this on top-level profiles*
693
    // (because it doesn't re-file child profiles)
694
570
    void set_name(const std::string& name) { _name = name; }
695
696
608
    int64_t metadata() const { return _metadata; }
697
4.03M
    void set_metadata(int64_t md) {
698
4.03M
        _is_set_metadata = true;
699
4.03M
        _metadata = md;
700
4.03M
    }
701
702
3.01M
    bool is_set_metadata() const { return _is_set_metadata; }
703
704
0
    time_t timestamp() const { return _timestamp; }
705
11
    void set_timestamp(time_t ss) { _timestamp = ss; }
706
707
    // Derived counter function: return measured throughput as input_value/second.
708
    static int64_t units_per_second(const Counter* total_counter, const Counter* timer);
709
710
    // Derived counter function: return aggregated value
711
    static int64_t counter_sum(const std::vector<Counter*>* counters);
712
713
    /// Adds a high water mark counter to the runtime profile. Otherwise, same behavior
714
    /// as AddCounter().
715
    HighWaterMarkCounter* AddHighWaterMarkCounter(
716
            const std::string& name, TUnit::type unit,
717
            const std::string& parent_counter_name = RuntimeProfile::ROOT_COUNTER,
718
            int64_t profile_level = 2);
719
720
    // Recursively compute the fraction of the 'total_time' spent in this profile and
721
    // its children.
722
    // This function updates _local_time_percent for each profile.
723
    void compute_time_in_profile();
724
725
    void clear_children();
726
727
private:
728
    // RuntimeProfileCounterTreeNode needs to access the counter map and child counter map
729
    friend class RuntimeProfileCounterTreeNode;
730
    // Pool for allocated counters. Usually owned by the creator of this
731
    // object, but occasionally allocated in the constructor.
732
    std::unique_ptr<ObjectPool> _pool;
733
734
    // Pool for allocated counters. These counters are shared with some other objects.
735
    std::map<std::string, std::shared_ptr<HighWaterMarkCounter>> _shared_counter_pool;
736
737
    // Name for this runtime profile.
738
    std::string _name;
739
740
    // user-supplied, uninterpreted metadata.
741
    int64_t _metadata;
742
    bool _is_set_metadata = false;
743
744
    bool _is_sink = false;
745
    bool _is_set_sink = false;
746
747
    // The timestamp when the profile was modified, make sure the update is up to date.
748
    time_t _timestamp;
749
750
    /// True if this profile is an average derived from other profiles.
751
    /// All counters in this profile must be of unit AveragedCounter.
752
    bool _is_averaged_profile;
753
754
    // Map from counter names to counters.  The profile owns the memory for the
755
    // counters.
756
    using CounterMap = std::map<std::string, Counter*>;
757
    CounterMap _counter_map;
758
759
    // Map from parent counter name to a set of child counter name.
760
    // All top level counters are the child of RuntimeProfile::ROOT_COUNTER (root).
761
    using ChildCounterMap = std::map<std::string, std::set<std::string>>;
762
    ChildCounterMap _child_counter_map;
763
764
    // protects _counter_map, _counter_child_map and _bucketing_counters
765
    mutable std::mutex _counter_map_lock;
766
767
    // Child profiles.  Does not own memory.
768
    // We record children in both a map (to facilitate updates) and a vector
769
    // (to print things in the order they were registered)
770
    using ChildMap = std::map<std::string, RuntimeProfile*>;
771
    ChildMap _child_map;
772
    // vector of (profile, indentation flag)
773
    using ChildVector = std::vector<std::pair<RuntimeProfile*, bool>>;
774
    ChildVector _children;
775
    mutable std::mutex _children_lock; // protects _child_map and _children
776
777
    using InfoStrings = std::map<std::string, std::string>;
778
    InfoStrings _info_strings;
779
780
    // Keeps track of the order in which InfoStrings are displayed when printed
781
    using InfoStringsDisplayOrder = std::vector<std::string>;
782
    InfoStringsDisplayOrder _info_strings_display_order;
783
784
    // Protects _info_strings and _info_strings_display_order
785
    mutable std::mutex _info_strings_lock;
786
787
    Counter _counter_total_time;
788
    // Time spent in just in this profile (i.e. not the children) as a fraction
789
    // of the total time in the entire profile tree.
790
    double _local_time_percent;
791
792
    // update a subtree of profiles from nodes, rooted at *idx.
793
    // On return, *idx points to the node immediately following this subtree.
794
    void update(const std::vector<TRuntimeProfileNode>& nodes, int* idx);
795
796
    // Similar to `void update(const std::vector<TRuntimeProfileNode>& nodes, int* idx)`
797
    void update(const google::protobuf::RepeatedPtrField<PRuntimeProfileNode>& nodes, int* idx);
798
799
    // Helper function to compute compute the fraction of the total time spent in
800
    // this profile and its children.
801
    // Called recursively.
802
    void compute_time_in_profile(int64_t total_time);
803
804
    // Print the child counters of the given counter name
805
    static void print_child_counters(const std::string& prefix, const std::string& counter_name,
806
                                     const CounterMap& counter_map,
807
                                     const ChildCounterMap& child_counter_map, std::ostream* s);
808
};
809
810
// Utility class to update the counter at object construction and destruction.
811
// When the object is constructed, decrement the counter by val.
812
// When the object goes out of scope, increment the counter by val.
813
class ScopedCounter {
814
public:
815
0
    ScopedCounter(RuntimeProfile::Counter* counter, int64_t val) : _val(val), _counter(counter) {
816
0
        if (counter == nullptr) {
817
0
            return;
818
0
        }
819
0
820
0
        _counter->update(-1L * _val);
821
0
    }
822
823
    // Increment the counter when object is destroyed
824
0
    ~ScopedCounter() {
825
0
        if (_counter != nullptr) {
826
0
            _counter->update(_val);
827
0
        }
828
0
    }
829
830
    // Disable copy constructor and assignment
831
    ScopedCounter(const ScopedCounter& counter) = delete;
832
    ScopedCounter& operator=(const ScopedCounter& counter) = delete;
833
834
private:
835
    int64_t _val;
836
    RuntimeProfile::Counter* _counter = nullptr;
837
};
838
839
// Utility class to update time elapsed when the object goes out of scope.
840
// 'T' must implement the stopWatch "interface" (start,stop,elapsed_time) but
841
// we use templates not to pay for virtual function overhead.
842
template <class T, typename Bool = bool>
843
class ScopedTimer {
844
public:
845
    ScopedTimer(RuntimeProfile::Counter* counter, const Bool* is_cancelled = nullptr)
846
104M
            : _counter(counter), _is_cancelled(is_cancelled) {
847
104M
        if (counter == nullptr) {
848
848k
            return;
849
848k
        }
850
104M
        DCHECK_EQ(counter->type(), TUnit::TIME_NS);
851
103M
        _sw.start();
852
103M
    }
_ZN5doris11ScopedTimerINS_15CustomStopWatchILi1EEEbEC2EPNS_14RuntimeProfile7CounterEPKb
Line
Count
Source
846
91.3M
            : _counter(counter), _is_cancelled(is_cancelled) {
847
91.3M
        if (counter == nullptr) {
848
848k
            return;
849
848k
        }
850
91.3M
        DCHECK_EQ(counter->type(), TUnit::TIME_NS);
851
90.4M
        _sw.start();
852
90.4M
    }
_ZN5doris11ScopedTimerINS_15CustomStopWatchILi3EEEbEC2EPNS_14RuntimeProfile7CounterEPKb
Line
Count
Source
846
13.1M
            : _counter(counter), _is_cancelled(is_cancelled) {
847
13.1M
        if (counter == nullptr) {
848
0
            return;
849
0
        }
850
13.1M
        DCHECK_EQ(counter->type(), TUnit::TIME_NS);
851
13.1M
        _sw.start();
852
13.1M
    }
853
854
    void stop() { _sw.stop(); }
855
856
    void start() { _sw.start(); }
857
858
104M
    bool is_cancelled() { return _is_cancelled != nullptr && *_is_cancelled; }
_ZN5doris11ScopedTimerINS_15CustomStopWatchILi1EEEbE12is_cancelledEv
Line
Count
Source
858
91.7M
    bool is_cancelled() { return _is_cancelled != nullptr && *_is_cancelled; }
_ZN5doris11ScopedTimerINS_15CustomStopWatchILi3EEEbE12is_cancelledEv
Line
Count
Source
858
13.2M
    bool is_cancelled() { return _is_cancelled != nullptr && *_is_cancelled; }
859
860
104M
    void UpdateCounter() {
861
105M
        if (_counter != nullptr && !is_cancelled()) {
862
105M
            _counter->update(_sw.elapsed_time());
863
105M
        }
864
104M
    }
_ZN5doris11ScopedTimerINS_15CustomStopWatchILi1EEEbE13UpdateCounterEv
Line
Count
Source
860
91.6M
    void UpdateCounter() {
861
91.7M
        if (_counter != nullptr && !is_cancelled()) {
862
91.7M
            _counter->update(_sw.elapsed_time());
863
91.7M
        }
864
91.6M
    }
_ZN5doris11ScopedTimerINS_15CustomStopWatchILi3EEEbE13UpdateCounterEv
Line
Count
Source
860
13.2M
    void UpdateCounter() {
861
13.2M
        if (_counter != nullptr && !is_cancelled()) {
862
13.2M
            _counter->update(_sw.elapsed_time());
863
13.2M
        }
864
13.2M
    }
865
866
    // Update counter when object is destroyed
867
105M
    ~ScopedTimer() {
868
105M
        if (_counter == nullptr) {
869
848k
            return;
870
848k
        }
871
104M
        _sw.stop();
872
104M
        UpdateCounter();
873
104M
    }
_ZN5doris11ScopedTimerINS_15CustomStopWatchILi1EEEbED2Ev
Line
Count
Source
867
91.9M
    ~ScopedTimer() {
868
91.9M
        if (_counter == nullptr) {
869
848k
            return;
870
848k
        }
871
91.1M
        _sw.stop();
872
91.1M
        UpdateCounter();
873
91.1M
    }
_ZN5doris11ScopedTimerINS_15CustomStopWatchILi3EEEbED2Ev
Line
Count
Source
867
13.2M
    ~ScopedTimer() {
868
13.2M
        if (_counter == nullptr) {
869
0
            return;
870
0
        }
871
13.2M
        _sw.stop();
872
13.2M
        UpdateCounter();
873
13.2M
    }
874
875
    // Disable copy constructor and assignment
876
    ScopedTimer(const ScopedTimer& timer) = delete;
877
    ScopedTimer& operator=(const ScopedTimer& timer) = delete;
878
879
private:
880
    T _sw;
881
    RuntimeProfile::Counter* _counter = nullptr;
882
    const Bool* _is_cancelled = nullptr;
883
};
884
885
// Utility class to update time elapsed when the object goes out of scope.
886
// 'T' must implement the stopWatch "interface" (start,stop,elapsed_time) but
887
// we use templates not to pay for virtual function overhead.
888
template <class T, class C>
889
class ScopedRawTimer {
890
public:
891
134M
    ScopedRawTimer(C* counter) : _counter(counter) { _sw.start(); }
_ZN5doris14ScopedRawTimerINS_15CustomStopWatchILi1EEElEC2EPl
Line
Count
Source
891
134M
    ScopedRawTimer(C* counter) : _counter(counter) { _sw.start(); }
_ZN5doris14ScopedRawTimerINS_15CustomStopWatchILi1EEESt6atomicIlEEC2EPS4_
Line
Count
Source
891
90.2k
    ScopedRawTimer(C* counter) : _counter(counter) { _sw.start(); }
892
    // Update counter when object is destroyed
893
135M
    ~ScopedRawTimer() { *_counter += _sw.elapsed_time(); }
_ZN5doris14ScopedRawTimerINS_15CustomStopWatchILi1EEElED2Ev
Line
Count
Source
893
135M
    ~ScopedRawTimer() { *_counter += _sw.elapsed_time(); }
_ZN5doris14ScopedRawTimerINS_15CustomStopWatchILi1EEESt6atomicIlEED2Ev
Line
Count
Source
893
90.1k
    ~ScopedRawTimer() { *_counter += _sw.elapsed_time(); }
894
895
    // Disable copy constructor and assignment
896
    ScopedRawTimer(const ScopedRawTimer& timer) = delete;
897
    ScopedRawTimer& operator=(const ScopedRawTimer& timer) = delete;
898
899
private:
900
    T _sw;
901
    C* _counter = nullptr;
902
};
903
} // namespace doris