summaryrefslogtreecommitdiff
path: root/absl/log/internal/conditions.h
blob: 41f67215a9c449f330a4d79e0f6fe76ed2f8493c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
// Copyright 2022 The Abseil Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// -----------------------------------------------------------------------------
// File: log/internal/conditions.h
// -----------------------------------------------------------------------------
//
// This file contains implementation of conditional log statements, like LOG_IF
// including all the ABSL_LOG_INTERNAL_..._CONDITION_... macros and
// various condition classes like LogEveryNState.

#ifndef ABSL_LOG_INTERNAL_CONDITIONS_H_
#define ABSL_LOG_INTERNAL_CONDITIONS_H_

#if defined(_WIN32) || defined(__hexagon__)
#include <cstdlib>
#else
#include <unistd.h>
#endif
#include <stdlib.h>

#include <atomic>
#include <cstdint>

#include "absl/base/attributes.h"
#include "absl/base/config.h"
#include "absl/log/internal/voidify.h"

// `ABSL_LOG_INTERNAL_CONDITION` prefixes another macro that expands to a
// temporary `LogMessage` instantiation followed by zero or more streamed
// expressions.  This definition is tricky to read correctly.  It evaluates to
// either
//
//   (void)0;
//
// or
//
//   ::absl::log_internal::Voidify() &&
//       ::absl::log_internal::LogMessage(...) << "the user's message";
//
// If the condition is evaluable at compile time, as is often the case, it
// compiles away to just one side or the other.
//
// Although this is not used anywhere a statement (e.g. `if`) could not go,
// the ternary expression does a better job avoiding spurious diagnostics
// (dangling else, missing switch case) and preserving noreturn semantics (e.g.
// on `LOG(FATAL)`) without requiring braces.
//
// The `switch` ensures that this expansion is the begnning of a statement (as
// opposed to an expression) and prevents shenanigans like
// `AFunction(LOG(INFO))` and `decltype(LOG(INFO))`.  The apparently-redundant
// `default` case makes the condition more amenable to Clang dataflow analysis.
#define ABSL_LOG_INTERNAL_STATELESS_CONDITION(condition) \
  switch (0)                                             \
  case 0:                                                \
  default:                                               \
    !(condition) ? (void)0 : ::absl::log_internal::Voidify()&&

// `ABSL_LOG_INTERNAL_STATEFUL_CONDITION` applies a condition like
// `ABSL_LOG_INTERNAL_CONDITION` but adds to that a series of variable
// declarations, including a local static object which stores the state needed
// to implement the stateful macros like `LOG_EVERY_N`.
//
// `for`-loops are used to declare scoped variables without braces (to permit
// streaming into the macro's expansion) and without the dangling-`else`
// problems/diagnostics that come with `if`.
//
// Two more variables are declared in separate `for`-loops:
//
// * `COUNTER` implements a streamable token whose value when streamed is the
//   number of times execution has passed through the macro.
// * A boolean flag is used to prevent any of the `for`-loops from ever actually
//   looping.
#define ABSL_LOG_INTERNAL_STATEFUL_CONDITION(condition)             \
  for (bool absl_log_internal_stateful_condition_do_log(condition); \
       absl_log_internal_stateful_condition_do_log;                 \
       absl_log_internal_stateful_condition_do_log = false)         \
  ABSL_LOG_INTERNAL_STATEFUL_CONDITION_IMPL
#define ABSL_LOG_INTERNAL_STATEFUL_CONDITION_IMPL(kind, ...)              \
  for (static ::absl::log_internal::Log##kind##State                      \
           absl_log_internal_stateful_condition_state;                    \
       absl_log_internal_stateful_condition_do_log &&                     \
       absl_log_internal_stateful_condition_state.ShouldLog(__VA_ARGS__); \
       absl_log_internal_stateful_condition_do_log = false)               \
    for (const uint32_t COUNTER ABSL_ATTRIBUTE_UNUSED =                   \
             absl_log_internal_stateful_condition_state.counter();        \
         absl_log_internal_stateful_condition_do_log;                     \
         absl_log_internal_stateful_condition_do_log = false)

// `ABSL_LOG_INTERNAL_CONDITION_*` serve to combine any conditions from the
// macro (e.g. `LOG_IF` or `VLOG`) with inherent conditions (e.g.
// `ABSL_MIN_LOG_LEVEL`) into a single boolean expression.  We could chain
// ternary operators instead, however some versions of Clang sometimes issue
// spurious diagnostics after such expressions due to a control flow analysis
// bug.
#ifdef ABSL_MIN_LOG_LEVEL
#define ABSL_LOG_INTERNAL_CONDITION_INFO(type, condition) \
  ABSL_LOG_INTERNAL_##type##_CONDITION(                   \
      (condition) && ::absl::LogSeverity::kInfo >=        \
                         static_cast<::absl::LogSeverity>(ABSL_MIN_LOG_LEVEL))
#define ABSL_LOG_INTERNAL_CONDITION_WARNING(type, condition) \
  ABSL_LOG_INTERNAL_##type##_CONDITION(                      \
      (condition) && ::absl::LogSeverity::kWarning >=        \
                         static_cast<::absl::LogSeverity>(ABSL_MIN_LOG_LEVEL))
#define ABSL_LOG_INTERNAL_CONDITION_ERROR(type, condition) \
  ABSL_LOG_INTERNAL_##type##_CONDITION(                    \
      (condition) && ::absl::LogSeverity::kError >=        \
                         static_cast<::absl::LogSeverity>(ABSL_MIN_LOG_LEVEL))
// NOTE: Use ternary operators instead of short-circuiting to mitigate
// https://bugs.llvm.org/show_bug.cgi?id=51928.
#define ABSL_LOG_INTERNAL_CONDITION_FATAL(type, condition)                 \
  ABSL_LOG_INTERNAL_##type##_CONDITION(                                    \
      ((condition)                                                         \
           ? (::absl::LogSeverity::kFatal >=                               \
                      static_cast<::absl::LogSeverity>(ABSL_MIN_LOG_LEVEL) \
                  ? true                                                   \
                  : (::absl::log_internal::AbortQuietly(), false))         \
           : false))
// NOTE: Use ternary operators instead of short-circuiting to mitigate
// https://bugs.llvm.org/show_bug.cgi?id=51928.
#define ABSL_LOG_INTERNAL_CONDITION_QFATAL(type, condition)                \
  ABSL_LOG_INTERNAL_##type##_CONDITION(                                    \
      ((condition)                                                         \
           ? (::absl::LogSeverity::kFatal >=                               \
                      static_cast<::absl::LogSeverity>(ABSL_MIN_LOG_LEVEL) \
                  ? true                                                   \
                  : (::absl::log_internal::ExitQuietly(), false))          \
           : false))
#define ABSL_LOG_INTERNAL_CONDITION_DFATAL(type, condition)             \
  ABSL_LOG_INTERNAL_##type##_CONDITION(                                 \
      (ABSL_ASSUME(absl::kLogDebugFatal == absl::LogSeverity::kError || \
                   absl::kLogDebugFatal == absl::LogSeverity::kFatal),  \
       (condition) &&                                                   \
           (::absl::kLogDebugFatal >=                                   \
                static_cast<::absl::LogSeverity>(ABSL_MIN_LOG_LEVEL) || \
            (::absl::kLogDebugFatal == ::absl::LogSeverity::kFatal &&   \
             (::absl::log_internal::AbortQuietly(), false)))))

#define ABSL_LOG_INTERNAL_CONDITION_LEVEL(severity)                    \
  for (int log_internal_severity_loop = 1; log_internal_severity_loop; \
       log_internal_severity_loop = 0)                                 \
    for (const absl::LogSeverity log_internal_severity =               \
             ::absl::NormalizeLogSeverity(severity);                   \
         log_internal_severity_loop; log_internal_severity_loop = 0)   \
  ABSL_LOG_INTERNAL_CONDITION_LEVEL_IMPL
#define ABSL_LOG_INTERNAL_CONDITION_LEVEL_IMPL(type, condition)    \
  ABSL_LOG_INTERNAL_##type##_CONDITION(                            \
      (condition) &&                                               \
      (log_internal_severity >=                                    \
           static_cast<::absl::LogSeverity>(ABSL_MIN_LOG_LEVEL) || \
       (log_internal_severity == ::absl::LogSeverity::kFatal &&    \
        (::absl::log_internal::AbortQuietly(), false))))
#else  // ndef ABSL_MIN_LOG_LEVEL
#define ABSL_LOG_INTERNAL_CONDITION_INFO(type, condition) \
  ABSL_LOG_INTERNAL_##type##_CONDITION(condition)
#define ABSL_LOG_INTERNAL_CONDITION_WARNING(type, condition) \
  ABSL_LOG_INTERNAL_##type##_CONDITION(condition)
#define ABSL_LOG_INTERNAL_CONDITION_ERROR(type, condition) \
  ABSL_LOG_INTERNAL_##type##_CONDITION(condition)
#define ABSL_LOG_INTERNAL_CONDITION_FATAL(type, condition) \
  ABSL_LOG_INTERNAL_##type##_CONDITION(condition)
#define ABSL_LOG_INTERNAL_CONDITION_QFATAL(type, condition) \
  ABSL_LOG_INTERNAL_##type##_CONDITION(condition)
#define ABSL_LOG_INTERNAL_CONDITION_DFATAL(type, condition) \
  ABSL_LOG_INTERNAL_##type##_CONDITION(condition)
#define ABSL_LOG_INTERNAL_CONDITION_LEVEL(severity)                    \
  for (int log_internal_severity_loop = 1; log_internal_severity_loop; \
       log_internal_severity_loop = 0)                                 \
    for (const absl::LogSeverity log_internal_severity =               \
             ::absl::NormalizeLogSeverity(severity);                   \
         log_internal_severity_loop; log_internal_severity_loop = 0)   \
  ABSL_LOG_INTERNAL_CONDITION_LEVEL_IMPL
#define ABSL_LOG_INTERNAL_CONDITION_LEVEL_IMPL(type, condition) \
  ABSL_LOG_INTERNAL_##type##_CONDITION(condition)
#endif  // ndef ABSL_MIN_LOG_LEVEL

namespace absl {
ABSL_NAMESPACE_BEGIN
namespace log_internal {

// Stateful condition class name should be "Log" + name + "State".
class LogEveryNState final {
 public:
  bool ShouldLog(int n);
  uint32_t counter() { return counter_.load(std::memory_order_relaxed); }

 private:
  std::atomic<uint32_t> counter_{0};
};

class LogFirstNState final {
 public:
  bool ShouldLog(int n);
  uint32_t counter() { return counter_.load(std::memory_order_relaxed); }

 private:
  std::atomic<uint32_t> counter_{0};
};

class LogEveryPow2State final {
 public:
  bool ShouldLog();
  uint32_t counter() { return counter_.load(std::memory_order_relaxed); }

 private:
  std::atomic<uint32_t> counter_{0};
};

class LogEveryNSecState final {
 public:
  bool ShouldLog(double seconds);
  uint32_t counter() { return counter_.load(std::memory_order_relaxed); }

 private:
  std::atomic<uint32_t> counter_{0};
  // Cycle count according to CycleClock that we should next log at.
  std::atomic<int64_t> next_log_time_cycles_{0};
};

// Helper routines to abort the application quietly

ABSL_ATTRIBUTE_NORETURN inline void AbortQuietly() { abort(); }
ABSL_ATTRIBUTE_NORETURN inline void ExitQuietly() { _exit(1); }
}  // namespace log_internal
ABSL_NAMESPACE_END
}  // namespace absl

#endif  // ABSL_LOG_INTERNAL_CONDITIONS_H_