Coverage Report

Created: 2026-08-21 05:47

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