YugabyteDB (2.13.1.0-b60, 21121d69985fbf76aa6958d8f04a9bfa936293b5)

Coverage Report

Created: 2022-03-22 16:43

/Users/deen/code/yugabyte-db/src/yb/util/stopwatch.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
//
18
// The following only applies to changes made to this file as part of YugaByte development.
19
//
20
// Portions Copyright (c) YugaByte, Inc.
21
//
22
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
23
// in compliance with the License.  You may obtain a copy of the License at
24
//
25
// http://www.apache.org/licenses/LICENSE-2.0
26
//
27
// Unless required by applicable law or agreed to in writing, software distributed under the License
28
// is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
29
// or implied.  See the License for the specific language governing permissions and limitations
30
// under the License.
31
//
32
#ifndef YB_UTIL_STOPWATCH_H
33
#define YB_UTIL_STOPWATCH_H
34
35
#include <glog/logging.h>
36
#include <sys/resource.h>
37
#include <sys/time.h>
38
#include <time.h>
39
#include <string>
40
#if defined(__APPLE__)
41
#include <mach/clock.h>
42
#include <mach/mach.h>
43
#endif  // defined(__APPLE__)
44
45
#include "yb/gutil/macros.h"
46
#include "yb/gutil/stringprintf.h"
47
#include "yb/gutil/walltime.h"
48
49
namespace yb {
50
51
// Macro for logging timing of a block. Usage:
52
//   LOG_TIMING_PREFIX_IF(INFO, FLAGS_should_record_time, "Tablet X: ", "doing some task") {
53
//     ... some task which takes some time
54
//   }
55
// If FLAGS_should_record_time is true, yields a log like:
56
// I1102 14:35:51.726186 23082 file.cc:167] Tablet X: Time spent doing some task:
57
//   real 3.729s user 3.570s sys 0.150s
58
// The task will always execute regardless of whether the timing information is
59
// printed.
60
#define LOG_TIMING_PREFIX_IF(severity, condition, prefix, description) \
61
435k
  for (yb::sw_internal::LogTiming _l(__FILE__, __LINE__, google::severity, prefix, description, \
62
870k
          -1, (condition)); !_l.HasRun(); 
_l.MarkHasRun()435k
)
63
64
// Conditionally log, no prefix.
65
#define LOG_TIMING_IF(severity, condition, description) \
66
18
  LOG_TIMING_PREFIX_IF(severity, (condition), "", (description))
67
68
// Always log, including prefix.
69
#define LOG_TIMING_PREFIX(severity, prefix, description) \
70
435k
  LOG_TIMING_PREFIX_IF(severity, true, (prefix), (description))
71
72
// Always log, no prefix.
73
#define LOG_TIMING(severity, description) \
74
18
  LOG_TIMING_IF(severity, true, (description))
75
76
// Macro to log the time spent in the rest of the block.
77
#define SCOPED_LOG_TIMING(severity, description) \
78
0
  yb::sw_internal::LogTiming VARNAME_LINENUM(_log_timing)(__FILE__, __LINE__, \
79
0
      google::severity, "", description, -1, true);
80
81
// Scoped version of LOG_SLOW_EXECUTION() but with a prefix.
82
#define SCOPED_LOG_SLOW_EXECUTION_PREFIX(severity, max_expected_millis, prefix, description) \
83
6.74M
  yb::sw_internal::LogTiming VARNAME_LINENUM(_log_timing)(__FILE__, __LINE__, \
84
6.74M
      google::severity, prefix, description, max_expected_millis, true)
85
86
// Macro for logging timing of a block. Usage:
87
//   LOG_SLOW_EXECUTION(INFO, 5, "doing some task") {
88
//     ... some task which takes some time
89
//   }
90
// when slower than 5 milliseconds, yields a log like:
91
// I1102 14:35:51.726186 23082 file.cc:167] Time spent doing some task:
92
//   real 3.729s user 3.570s sys 0.150s
93
#define LOG_SLOW_EXECUTION(severity, max_expected_millis, description) \
94
29.0M
  for (yb::sw_internal::LogTiming _l(__FILE__, __LINE__, google::severity, "", description, \
95
57.5M
          max_expected_millis, true); !_l.HasRun(); 
_l.MarkHasRun()28.5M
)
96
97
// Macro for vlogging timing of a block. The execution happens regardless of the vlog_level,
98
// it's only the logging that's affected.
99
// Usage:
100
//   VLOG_TIMING(1, "doing some task") {
101
//     ... some task which takes some time
102
//   }
103
// Yields a log just like LOG_TIMING's.
104
#define VLOG_TIMING(vlog_level, description) \
105
2.60k
  for (yb::sw_internal::LogTiming _l(__FILE__, __LINE__, google::INFO, "", description, \
106
2.69k
          -1, VLOG_IS_ON(vlog_level)); !_l.HasRun(); 
_l.MarkHasRun()94
)
107
108
// Macro to log the time spent in the rest of the block.
109
#define SCOPED_VLOG_TIMING(vlog_level, description) \
110
  yb::sw_internal::LogTiming VARNAME_LINENUM(_log_timing)(__FILE__, __LINE__, \
111
      google::INFO, "", description, -1, VLOG_IS_ON(vlog_level));
112
113
2.53M
#define NANOS_PER_SECOND 1000000000.0
114
36.2M
#define NANOS_PER_MILLISECOND 1000000.0
115
116
class Stopwatch;
117
118
typedef uint64_t nanosecond_type;
119
120
// Structure which contains an elapsed amount of wall/user/sys time.
121
struct CpuTimes {
122
  nanosecond_type wall;
123
  nanosecond_type user;
124
  nanosecond_type system;
125
126
36.3M
  void clear() { wall = user = system = 0LL; }
127
128
  // Return a string formatted similar to the output of the "time" shell command.
129
441k
  std::string ToString() const {
130
441k
    return StringPrintf(
131
441k
      "real %.3fs\tuser %.3fs\tsys %.3fs",
132
441k
      wall_seconds(), user_cpu_seconds(), system_cpu_seconds());
133
441k
  }
134
135
36.2M
  double wall_millis() const {
136
36.2M
    return static_cast<double>(wall) / NANOS_PER_MILLISECOND;
137
36.2M
  }
138
139
1.65M
  double wall_seconds() const {
140
1.65M
    return static_cast<double>(wall) / NANOS_PER_SECOND;
141
1.65M
  }
142
143
441k
  double user_cpu_seconds() const {
144
441k
    return static_cast<double>(user) / NANOS_PER_SECOND;
145
441k
  }
146
147
441k
  double system_cpu_seconds() const {
148
441k
    return static_cast<double>(system) / NANOS_PER_SECOND;
149
441k
  }
150
};
151
152
// A Stopwatch is a convenient way of timing a given operation.
153
//
154
// Wall clock time is based on a monotonic timer, so can be reliably used for
155
// determining durations.
156
// CPU time is based on the current thread's usage (not the whole process).
157
//
158
// The implementation relies on several syscalls, so should not be used for
159
// hot paths, but is useful for timing anything on the granularity of seconds
160
// or more.
161
class Stopwatch {
162
 public:
163
164
  enum Mode {
165
    // Collect usage only about the calling thread.
166
    // This may not be supported on older versions of Linux.
167
    THIS_THREAD,
168
    // Collect usage of all threads.
169
    ALL_THREADS
170
  };
171
172
  // Construct a new stopwatch. The stopwatch is initially stopped.
173
  explicit Stopwatch(Mode mode = THIS_THREAD)
174
    : stopped_(true),
175
36.3M
      mode_(mode) {
176
36.3M
    times_.clear();
177
36.3M
  }
178
179
  // Start counting. If the stopwatch is already counting, then resets the
180
  // start point at the current time.
181
36.3M
  void start() {
182
36.3M
    stopped_ = false;
183
36.3M
    GetTimes(&times_);
184
36.3M
  }
185
186
  // Stop counting. If the stopwatch is already stopped, has no effect.
187
36.2M
  void stop() {
188
36.2M
    if (stopped_) 
return0
;
189
36.2M
    stopped_ = true;
190
191
36.2M
    CpuTimes current;
192
36.2M
    GetTimes(&current);
193
36.2M
    times_.wall = current.wall - times_.wall;
194
36.2M
    times_.user = current.user - times_.user;
195
36.2M
    times_.system = current.system - times_.system;
196
36.2M
  }
197
198
  // Return the elapsed amount of time. If the stopwatch is running, then returns
199
  // the amount of time since it was started. If it is stopped, returns the amount
200
  // of time between the most recent start/stop pair. If the stopwatch has never been
201
  // started, the elapsed time is considered to be zero.
202
37.4M
  CpuTimes elapsed() const {
203
37.4M
    if (stopped_) 
return times_36.2M
;
204
205
1.20M
    CpuTimes current;
206
1.20M
    GetTimes(&current);
207
1.20M
    current.wall -= times_.wall;
208
1.20M
    current.user -= times_.user;
209
1.20M
    current.system -= times_.system;
210
1.20M
    return current;
211
37.4M
  }
212
213
  // Resume a stopped stopwatch, such that the elapsed time continues to grow from
214
  // the point where it was last stopped.
215
  // For example:
216
  //   Stopwatch s;
217
  //   s.start();
218
  //   sleep(1); // elapsed() is now ~1sec
219
  //   s.stop();
220
  //   sleep(1);
221
  //   s.resume();
222
  //   sleep(1); // elapsed() is now ~2sec
223
0
  void resume() {
224
0
    if (!stopped_) return;
225
0
226
0
    CpuTimes current(times_);
227
0
    start();
228
0
    times_.wall   -= current.wall;
229
0
    times_.user   -= current.user;
230
0
    times_.system -= current.system;
231
0
  }
232
233
0
  bool is_stopped() const {
234
0
    return stopped_;
235
0
  }
236
237
 private:
238
73.7M
  void GetTimes(CpuTimes *times) const {
239
73.7M
    struct rusage usage;
240
73.7M
    struct timespec wall;
241
242
73.7M
    MicrosecondsInt64 user_cpu_us = 0;
243
73.7M
    MicrosecondsInt64 sys_cpu_us = 0;
244
73.7M
#if defined(__APPLE__)
245
73.8M
    if (
mode_ == THIS_THREAD73.7M
) {
246
73.8M
      GetThreadUserAndSysCpuTimeMicros(&user_cpu_us, &sys_cpu_us);
247
18.4E
    } else {
248
18.4E
      CHECK_EQ(0, getrusage(RUSAGE_SELF, &usage));
249
18.4E
      user_cpu_us = usage.ru_utime.tv_sec * 1e6L + usage.ru_utime.tv_usec;
250
18.4E
      sys_cpu_us = usage.ru_stime.tv_sec * 1e6L + usage.ru_stime.tv_usec;
251
18.4E
    }
252
253
73.7M
    mach_timespec_t ts;
254
73.7M
    walltime_internal::GetCurrentTime(&ts);
255
73.7M
    wall.tv_sec = ts.tv_sec;
256
73.7M
    wall.tv_nsec = ts.tv_nsec;
257
#else
258
    CHECK_EQ(0, getrusage((mode_ == THIS_THREAD) ? RUSAGE_THREAD : RUSAGE_SELF, &usage));
259
    CHECK_EQ(0, clock_gettime(CLOCK_MONOTONIC, &wall));
260
    user_cpu_us = usage.ru_utime.tv_sec * 1e6L + usage.ru_utime.tv_usec;
261
    sys_cpu_us = usage.ru_stime.tv_sec * 1e6L + usage.ru_stime.tv_usec;
262
#endif  // defined(__APPLE__)
263
264
73.7M
    times->wall   = wall.tv_sec * 1000000000L + wall.tv_nsec;
265
73.7M
    times->user   = user_cpu_us * 1000; // Convert to nanoseconds.
266
73.7M
    times->system = sys_cpu_us * 1000;  // Convert to nanoseconds.
267
73.7M
  }
268
269
  bool stopped_;
270
271
  CpuTimes times_;
272
  Mode mode_;
273
};
274
275
276
namespace sw_internal {
277
278
// Internal class used by the LOG_TIMING macro.
279
class LogTiming {
280
 public:
281
  LogTiming(const char *file, int line, google::LogSeverity severity,
282
            std::string prefix, std::string description,
283
            int64_t max_expected_millis, bool should_print)
284
      : file_(file),
285
        line_(line),
286
        severity_(severity),
287
        prefix_(std::move(prefix)),
288
        description_(std::move(description)),
289
        max_expected_millis_(max_expected_millis),
290
        should_print_(should_print),
291
36.2M
        has_run_(false) {
292
36.2M
    stopwatch_.start();
293
36.2M
  }
294
295
36.2M
  ~LogTiming() {
296
36.2M
    if (should_print_) {
297
36.2M
      Print(max_expected_millis_);
298
36.2M
    }
299
36.2M
  }
300
301
  // Allows this object to be used as the loop variable in for-loop macros.
302
  // Call HasRun() in the conditional check in the for-loop.
303
58.4M
  bool HasRun() {
304
58.4M
    return has_run_;
305
58.4M
  }
306
307
  // Allows this object to be used as the loop variable in for-loop macros.
308
  // Call MarkHasRun() in the "increment" section of the for-loop.
309
28.9M
  void MarkHasRun() {
310
28.9M
    has_run_ = true;
311
28.9M
  }
312
313
 private:
314
  Stopwatch stopwatch_;
315
  const char *file_;
316
  const int line_;
317
  const google::LogSeverity severity_;
318
  const string prefix_;
319
  const std::string description_;
320
  const int64_t max_expected_millis_;
321
  const bool should_print_;
322
  bool has_run_;
323
324
  // Print if the number of expected millis exceeds the max.
325
  // Passing a negative number implies "always print".
326
36.2M
  void Print(int64_t max_expected_millis) {
327
36.2M
    stopwatch_.stop();
328
36.2M
    CpuTimes times = stopwatch_.elapsed();
329
36.2M
    if (times.wall_millis() > max_expected_millis) {
330
440k
      google::LogMessage(file_, line_, severity_).stream()
331
440k
        << prefix_ << "Time spent " << description_ << ": "
332
440k
        << times.ToString();
333
440k
    }
334
36.2M
  }
335
336
};
337
338
} // namespace sw_internal
339
} // namespace yb
340
341
#endif  // YB_UTIL_STOPWATCH_H