• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

PowerDNS / pdns / 19765062687

28 Nov 2025 01:22PM UTC coverage: 73.135% (+0.03%) from 73.105%
19765062687

Pull #16574

github

web-flow
Merge cc585a1f8 into 8b4e32eff
Pull Request #16574: Improve GitHub error highlighting

38564 of 63428 branches covered (60.8%)

Branch coverage included in aggregate %.

128155 of 164534 relevant lines covered (77.89%)

6255827.11 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

58.41
/pdns/recursordist/rec-eventtrace.hh
1
/*
2
 * This file is part of PowerDNS or dnsdist.
3
 * Copyright -- PowerDNS.COM B.V. and its contributors
4
 *
5
 * This program is free software; you can redistribute it and/or modify
6
 * it under the terms of version 2 of the GNU General Public License as
7
 * published by the Free Software Foundation.
8
 *
9
 * In addition, for the avoidance of any doubt, permission is granted to
10
 * link this program with OpenSSL and to (re)distribute the binaries
11
 * produced as the result of such linking.
12
 *
13
 * This program is distributed in the hope that it will be useful,
14
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16
 * GNU General Public License for more details.
17
 *
18
 * You should have received a copy of the GNU General Public License
19
 * along with this program; if not, write to the Free Software
20
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21
 */
22
#pragma once
23

24
#include "misc.hh"
25
#include "noinitvector.hh"
26

27
#include <optional>
28
#include <unordered_map>
29
#include <variant>
30
#include "protozero-trace.hh"
31

32
class RecEventTrace
33
{
34
public:
35
  enum EventType : uint8_t
36
  {
37
    // Keep in-syc with dnsmessagge.proto!
38
    // Don't forget to add a new entry to the table in the .cc file!
39
    // Generic events
40
    CustomEvent = 0,
41
    ReqRecv = 1,
42
    PCacheCheck = 2,
43
    AnswerSent = 3,
44

45
    // Recursor specific events
46
    SyncRes = 100,
47
    LuaGetTag = 101,
48
    LuaGetTagFFI = 102,
49
    LuaIPFilter = 103,
50
    LuaPreRPZ = 104,
51
    LuaPreResolve = 105,
52
    LuaPreOutQuery = 106,
53
    LuaPostResolve = 107,
54
    LuaNoData = 108,
55
    LuaNXDomain = 109,
56
    LuaPostResolveFFI = 110,
57

58
    AuthRequest = 120,
59
    PacketParse = 121,
60
    ProcessUDP = 122,
61
    ProcessTCP = 123,
62
  };
63

64
  static const std::unordered_map<EventType, std::string> s_eventNames;
65

66
  RecEventTrace()
67
  {
9,308✔
68
    reset();
9,308✔
69
  }
9,308✔
70

71
  RecEventTrace(const RecEventTrace& old) :
72
    d_events(old.d_events),
73
    d_base(old.d_base),
74
    d_status(old.d_status),
75
    d_OTTrace(old.d_OTTrace)
76
  {
7,226✔
77
    // An RecEventTrace object can be copied, but the original will be marked invalid.
78
    // This is do detect (very likely) unintended modifications to the original after
79
    // the ownership changed.
80
    old.d_status = Invalid;
7,226✔
81
  }
7,226✔
82

83
  RecEventTrace(RecEventTrace&& old) noexcept :
84
    d_events(std::move(old.d_events)),
85
    d_base(old.d_base),
86
    d_status(old.d_status),
87
    d_OTTrace(old.d_OTTrace)
88
  {
3,613✔
89
    // An RecEventTrace object can be moved, but the original will be marked invalid.
90
    // This is do detect (very likely) unintended modifications to the original after
91
    // the ownership changed.
92
    old.d_status = Invalid;
3,613✔
93
  }
3,613✔
94

95
  RecEventTrace& operator=(const RecEventTrace& old) = delete;
96
  RecEventTrace& operator=(RecEventTrace&& old) noexcept
97
  {
5,665✔
98
    d_events = std::move(old.d_events);
5,665✔
99
    d_base = old.d_base;
5,665✔
100
    d_status = old.d_status;
5,665✔
101
    d_OTTrace = old.d_OTTrace;
5,665✔
102
    old.d_status = Invalid;
5,665✔
103
    return *this;
5,665✔
104
  }
5,665✔
105

106
  ~RecEventTrace() = default;
20,127✔
107

108
  // We distinguish between strings and byte arrays. Does not matter in C++, but in Go, Java etc it does
109
  using Value_t = std::variant<std::nullopt_t, bool, int64_t, std::string, PacketBuffer>;
110

111
  static std::string toString(const EventType eventType)
112
  {
108✔
113
    return s_eventNames.at(eventType);
108✔
114
  }
108✔
115

116
  static std::string toString(const Value_t& value)
117
  {
×
118
    if (std::holds_alternative<std::nullopt_t>(value)) {
×
119
      return "";
×
120
    }
×
121
    if (std::holds_alternative<bool>(value)) {
×
122
      return std::to_string(std::get<bool>(value));
×
123
    }
×
124
    if (std::holds_alternative<int64_t>(value)) {
×
125
      return std::to_string(std::get<int64_t>(value));
×
126
    }
×
127
    if (std::holds_alternative<std::string>(value)) {
×
128
      return std::get<std::string>(value);
×
129
    }
×
130
    if (std::holds_alternative<PacketBuffer>(value)) {
×
131
      const auto& packet = std::get<PacketBuffer>(value);
×
132
      return makeHexDump(std::string(reinterpret_cast<const char*>(packet.data()), packet.size())); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
×
133
    }
×
134
    return "?";
×
135
  }
×
136

137
  struct Entry
138
  {
139
    Entry(Value_t&& value, EventType eventType, bool start, int64_t timestamp, size_t parent, size_t match) :
140
      d_value(std::move(value)), d_ts(timestamp), d_parent(parent), d_matching(match), d_event(eventType), d_start(start)
141
    {
281✔
142
    }
281✔
143
    Entry(Value_t&& value, std::string custom, bool start, int64_t timestamp, size_t parent, size_t match) :
144
      d_value(std::move(value)), d_custom(std::move(custom)), d_ts(timestamp), d_parent(parent), d_matching(match), d_event(CustomEvent), d_start(start)
145
    {
×
146
    }
×
147
    Value_t d_value;
148
    std::vector<std::pair<string, Value_t>> d_extraValues;
149
    std::string d_custom;
150
    std::string d_valueName{"arg"};
151
    int64_t d_ts;
152
    size_t d_parent;
153
    size_t d_matching;
154
    EventType d_event;
155
    bool d_start;
156

157
    [[nodiscard]] std::string toString() const
158
    {
×
159
      std::string value = RecEventTrace::toString(d_value);
×
160
      if (!value.empty()) {
×
161
        value = "," + value;
×
162
      }
×
163
      std::string name = RecEventTrace::toString(d_event);
×
164
      if (d_event == EventType::CustomEvent) {
×
165
        name += ":" + d_custom;
×
166
      }
×
167

168
      return name + "(" + std::to_string(d_ts) + value + (d_start ? ")" : ",done)");
×
169
    }
×
170
  };
171

172
  void setEnabled(bool flag)
173
  {
6,322✔
174
    assert(d_status != Invalid);
6,322✔
175
    d_status = flag ? Enabled : Disabled;
6,322✔
176
  }
6,322✔
177

178
  bool enabled() const
179
  {
44,652✔
180
    return d_status == Enabled;
44,652✔
181
  }
44,652✔
182

183
  template <class E>
184
  size_t add(E event, Value_t&& value, bool start, size_t match, int64_t stamp = 0)
185
  {
75,814✔
186
    assert(d_status != Invalid);
75,814✔
187
    if (d_status == Disabled) {
75,828✔
188
      return 0;
75,520✔
189
    }
75,520✔
190

191
    if (stamp == 0) {
2,147,483,928✔
192
      struct timespec theTime{};
281✔
193
      clock_gettime(CLOCK_MONOTONIC, &theTime);
281✔
194
      stamp = theTime.tv_nsec + theTime.tv_sec * 1000000000;
281✔
195
    }
281✔
196
    if (stamp < d_base) {
2,147,483,928!
197
      // If we get a ts before d_base, we adjust d_base and the existing events
198
      // This is possible if we add a kernel provided packet timestamp in the future
199
      // (Though it seems those timestamps do not use CLOCK_MONOTONIC...)
200
      const int64_t adj = d_base - stamp;
×
201
      for (auto& iter : d_events) {
×
202
        iter.d_ts += adj;
×
203
      }
×
204
      // and move to the new base
205
      d_base = stamp;
×
206
    }
×
207
    stamp -= d_base;
2,147,483,928✔
208
    d_events.emplace_back(std::move(value), event, start, stamp, d_parent, match);
2,147,483,928✔
209
    return d_events.size() - 1;
2,147,483,928✔
210
  }
75,814✔
211

212
  template <class E>
213
  size_t add(E eventType)
214
  {
30,216✔
215
    return add(eventType, Value_t(std::nullopt), true, 0, 0);
30,216✔
216
  }
30,216✔
217

218
  // We store uint32 in an int64_t
219
  template <class E>
220
  size_t add(E eventType, uint32_t value, bool start, size_t match)
221
  {
121✔
222
    return add(eventType, static_cast<int64_t>(value), start, match, 0);
121✔
223
  }
121✔
224
  // We store int32 in an int64_t
225
  template <class E>
226
  size_t add(E eventType, int32_t value, bool start, size_t match)
227
  {
11,808✔
228
    return add(eventType, static_cast<int64_t>(value), start, match, 0);
11,808✔
229
  }
11,808✔
230

231
  template <class E, class T>
232
  size_t add(E eventType, T value, bool start, size_t match)
233
  {
33,687✔
234
    return add(eventType, Value_t(value), start, match, 0);
33,687✔
235
  }
33,687✔
236

237
  void setValueName(size_t index, const std::string& name)
238
  {
13,235✔
239
    assert(d_status != Invalid);
13,235✔
240
    if (d_status == Disabled) {
13,235!
241
      return;
13,235✔
242
    }
13,235✔
243
    d_events.at(index).d_valueName = name;
×
244
  }
×
245

246
  void addExtraValues(size_t index, std::vector<std::pair<std::string, Value_t>>&& values)
247
  {
13,236✔
248
    assert(d_status != Invalid);
13,236✔
249
    if (d_status == Disabled) {
13,236✔
250
      return;
13,234✔
251
    }
13,234✔
252
    d_events.at(index).d_extraValues = std::move(values);
2✔
253
  }
2✔
254

255
  void clear()
256
  {
5,424✔
257
    d_events.clear();
5,424✔
258
    reset();
5,424✔
259
  }
5,424✔
260

261
  void reset()
262
  {
14,726✔
263
    struct timespec theTime{};
14,726✔
264
    clock_gettime(CLOCK_MONOTONIC, &theTime);
14,726✔
265
    d_base = theTime.tv_nsec + theTime.tv_sec * 1000000000;
14,726✔
266
    d_OTTrace = false;
14,726✔
267
    d_status = Disabled;
14,726✔
268
  }
14,726✔
269

270
  std::string toString() const
271
  {
×
272
    assert(d_status != Invalid);
×
273
    if (d_status == Disabled) {
×
274
      return "Disabled\n";
×
275
    }
×
276
    std::string ret = "eventTrace [";
×
277
    bool first = true;
×
278
    for (const auto& event : d_events) {
×
279
      if (first) {
×
280
        first = false;
×
281
      }
×
282
      else {
×
283
        ret += "; ";
×
284
      }
×
285
      ret += event.toString();
×
286
    }
×
287
    ret += ']';
×
288
    return ret;
×
289
  }
×
290

291
  const std::vector<Entry>& getEvents() const
292
  {
×
293
    return d_events;
×
294
  }
×
295

296
  std::vector<pdns::trace::Span> convertToOT(const pdns::trace::InitialSpanInfo& span) const;
297

298
  size_t setParent(size_t parent)
299
  {
10,781✔
300
    size_t old = d_parent;
10,781✔
301
    d_parent = parent;
10,781✔
302
    return old;
10,781✔
303
  }
10,781✔
304

305
  bool getThisOTTraceEnabled() const
306
  {
21✔
307
    return d_OTTrace;
21✔
308
  }
21✔
309

310
  void setThisOTTraceEnabled()
311
  {
21✔
312
    d_OTTrace = true;
21✔
313
  }
21✔
314

315
  // The EventScope class is used to close (add an end event) automatically upon the scope object
316
  // going out of scope. It is also possible to manually close it, specifying a value to be registered
317
  // at the close event. In that case the dt call will become a no-op.
318
  class EventScope
319
  {
320
  public:
321
    EventScope(size_t oldParent, RecEventTrace& eventTrace) :
322
      d_eventTrace(eventTrace),
323
      d_oldParent(oldParent)
324
    {
10,768✔
325
      if (d_eventTrace.enabled() && !d_eventTrace.d_events.empty()) {
10,768!
326
        d_event = d_eventTrace.d_events.back().d_event;
40✔
327
        d_match = d_eventTrace.d_events.size() - 1;
40✔
328
      }
40✔
329
    }
10,768✔
330

331
    // Only int64_t for now needed, might become a template in the future.
332
    void close(int64_t val)
333
    {
21,287✔
334
      if (!d_eventTrace.enabled() || d_closed) {
21,291✔
335
        return;
21,248✔
336
      }
21,248✔
337
      d_eventTrace.setParent(d_oldParent);
2,147,483,671✔
338
      d_eventTrace.add(d_event, val, false, d_match);
2,147,483,671✔
339
      d_closed = true;
2,147,483,671✔
340
    }
2,147,483,671✔
341

342
    ~EventScope()
343
    {
10,754✔
344
      // If the dt is called after an explicit close(), value does not matter.
345
      // Otherwise, it signals an implicit close, e.g. an exception was thrown
346
      close(-1);
10,754✔
347
    }
10,754✔
348
    EventScope(const EventScope&) = delete;
349
    EventScope(EventScope&&) = delete;
350
    EventScope& operator=(const EventScope&) = delete;
351
    EventScope& operator=(EventScope&&) = delete;
352

353
  private:
354
    RecEventTrace& d_eventTrace;
355
    size_t d_oldParent;
356
    size_t d_match{0};
357
    EventType d_event{EventType::CustomEvent};
358
    bool d_closed{false};
359
  };
360

361
private:
362
  std::vector<Entry> d_events;
363
  int64_t d_base{0};
364
  size_t d_parent{0};
365
  enum Status : uint8_t
366
  {
367
    Disabled,
368
    Invalid,
369
    Enabled
370
  };
371
  mutable Status d_status{Disabled};
372
  bool d_OTTrace{false};
373
};
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc