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

Alan-Jowett / libbtf / 18665930710

20 Oct 2025 09:52PM UTC coverage: 96.115% (+0.8%) from 95.331%
18665930710

Pull #173

github

web-flow
Merge d80f4a586 into b6741487e
Pull Request #173: Add support for correctly handling BTF data with cycles

883 of 925 new or added lines in 6 files covered. (95.46%)

1 existing line in 1 file now uncovered.

2177 of 2265 relevant lines covered (96.11%)

1581.57 hits per line

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

94.89
/libbtf/btf_json.cpp
1
// Copyright (c) Prevail Verifier contributors.
2
// SPDX-License-Identifier: MIT
3

4
#include "btf_json.h"
5

6
#include "btf_c_type.h"
7
#include "cycle_detector.h"
8

9
#include <functional>
10
#include <set>
11

12
namespace libbtf {
13
std::string pretty_print_json(const std::string &input) {
90✔
14
  // Walk over the input string, inserting newlines and indentation.
15
  std::string output;
45✔
16
  int indent = 0;
45✔
17
  bool in_string = false;
45✔
18
  for (size_t i = 0; i < input.size(); i++) {
282,168✔
19
    char c = input[i];
282,078✔
20
    if (c == '"') {
282,078✔
21
      in_string = !in_string;
39,900✔
22
    }
23
    if (in_string) {
282,078✔
24
      output += c;
206,694✔
25
      continue;
206,694✔
26
    }
27
    switch (c) {
75,384✔
28
    case '{':
3,442✔
29
    case '[':
30
      output += c;
1,721✔
31
      if (i + 1 < input.size() && input[i + 1] != '}' && input[i + 1] != ']') {
3,442✔
32
        output += '\n';
1,721✔
33
        indent += 2;
3,442✔
34
        output += std::string(indent, ' ');
5,163✔
35
      } else {
36
        output += input[++i];
×
37
      }
38
      break;
1,721✔
39
    case '}':
1,721✔
40
    case ']':
41
      output += '\n';
1,721✔
42
      indent -= 2;
3,442✔
43
      output += std::string(indent, ' ');
5,163✔
44
      output += c;
1,721✔
45
      break;
1,721✔
46
    case ',':
12,768✔
47
      output += c;
6,384✔
48
      output += '\n';
6,384✔
49
      output += std::string(indent, ' ');
12,768✔
50
      break;
12,768✔
51
    case ':':
15,324✔
52
      output += c;
7,662✔
53
      output += ' ';
7,662✔
54
      break;
7,662✔
55
    default:
40,408✔
56
      output += c;
20,204✔
57
      break;
20,204✔
58
    }
59
  }
60
  return output;
90✔
61
}
62

63
template <typename T>
64
static void _print_json_value(bool &first, const std::string &name, T value,
13,140✔
65
                              std::ostream &out) {
66
  // If T is a string type, print it as a string, then quote the value.
67
  if constexpr (std::is_same_v<T, std::string> ||
68
                std::is_same_v<T, const char *> || std::is_same_v<T, char *>) {
69
    out << (first ? "" : ",") << "\"" << name << "\":\"" << value << "\"";
8,133✔
70
    first = false;
4,096✔
71
  }
72
  // If T is a bool, print it as a string, then quote the value.
73
  else if constexpr (std::is_same_v<T, bool>) {
74
    out << (first ? "" : ",") << "\"" << name
75
        << "\":" << (value ? "true" : "false");
6,129✔
76
    first = false;
2,504✔
77
  }
78
  // If T is a std::optional<std::string>, then only print if it's present
79
  else if constexpr (std::is_same_v<T, std::optional<std::string>>) {
80
    if (value.has_value()) {
698✔
81
      out << (first ? "" : ",") << "\"" << name << "\":\"" << value.value()
612✔
82
          << "\"";
940✔
83
      first = false;
612✔
84
    }
85
  } else {
86
    out << (first ? "" : ",") << "\"" << name << "\":" << std::to_string(value);
10,492✔
87
    first = false;
5,842✔
88
  }
89
}
13,140✔
90

91
void print_array_start(const std::string &name, std::ostream &out) {
370✔
92
  out << "\"" << name << "\":[";
370✔
93
}
370✔
94

95
void print_array_end(std::ostream &out) { out << "]"; }
239✔
96

97
#define PRINT_JSON_FIXED(name, value)                                          \
98
  _print_json_value(first, name, value, out);
99

100
#define PRINT_JSON_VALUE(object, value)                                        \
101
  _print_json_value(first, #value, object.value, out)
102

103
#define PRINT_JSON_TYPE(object, value)                                         \
104
  if (!first) {                                                                \
105
    out << ",";                                                                \
106
  } else {                                                                     \
107
    first = false;                                                             \
108
  };                                                                           \
109
  out << "\"" << #value << "\":";                                              \
110
  print_btf_kind(object.value, id_to_kind.at(object.value));
111

112
#define PRINT_JSON_ARRAY_START(object, value)                                  \
113
  if (!first) {                                                                \
114
    out << ",";                                                                \
115
  } else {                                                                     \
116
    first = false;                                                             \
117
  }                                                                            \
118
  print_array_start(#value, out);                                              \
119
  {                                                                            \
120
    bool first = true;
121

122
#define PRINT_JSON_ARRAY_END()                                                 \
123
  print_array_end(out);                                                        \
124
  }
125

126
#define PRINT_JSON_OBJECT_START()                                              \
127
  if (!first) {                                                                \
128
    out << ",";                                                                \
129
  } else {                                                                     \
130
    first = false;                                                             \
131
  };                                                                           \
132
  {                                                                            \
133
    bool first = true;                                                         \
134
    out << "{";
135

136
#define PRINT_JSON_OBJECT_END()                                                \
137
  out << "}";                                                                  \
138
  }
139

140
#define PRINT_JSON_VALUE_IF_PRESENT(object, value)                             \
141
  if constexpr (btf_kind_traits<decltype(object)>::has_##value) {              \
142
    PRINT_JSON_VALUE(object, value);                                           \
143
  }
144

145
#define PRINT_JSON_TYPE_IF_PRESENT(object, value)                              \
146
  if constexpr (btf_kind_traits<decltype(object)>::has_##value) {              \
147
    PRINT_JSON_TYPE(object, value);                                            \
148
  }
149

150
// Suppress C4456 on when using MSVC:
151
// declaration of 'first' hides previous local declaration
152
#pragma warning(push)
153
#pragma warning(disable : 4456)
154
#pragma warning(disable : 4458)
155

156
void btf_type_to_json(const std::map<btf_type_id, btf_kind> &id_to_kind,
108✔
157
                      std::ostream &out,
158
                      std::optional<std::function<bool(btf_type_id)>> filter) {
159
  cycle_detector detector; // Track cycles using cycle_detector
54✔
160
  using scoped_visit = libbtf::scoped_visit;
161

162
  std::function<void(btf_type_id, const btf_kind &)> print_btf_kind =
163
      [&](btf_type_id id, const btf_kind &kind) {
2,316✔
164
        // Use scoped_visit for automatic cycle detection and cleanup
165
        scoped_visit visit(detector, id, std::nothrow);
2,316✔
166

167
        // Check for cycles using scoped_visit
168
        if (!visit.is_marked()) {
2,316✔
169
          // We're in a cycle - print just a reference
170
          bool first = true;
NEW
171
          PRINT_JSON_OBJECT_START();
×
NEW
172
          PRINT_JSON_FIXED("id", id);
×
NEW
173
          PRINT_JSON_FIXED("kind_type", "reference");
×
NEW
174
          PRINT_JSON_OBJECT_END();
×
175
          return;
176
        }
177

178
        // No cycle detected, proceed with normal printing
179
        // The scoped_visit will automatically unmark when it goes out of scope
180

181
        bool first = true;
1,158✔
182
        PRINT_JSON_OBJECT_START();
2,316✔
183
        PRINT_JSON_FIXED("id", id);
3,474✔
184
        PRINT_JSON_FIXED("kind_type",
2,316✔
185
                         BTF_KIND_INDEX_TO_STRING(
186
                             static_cast<btf_kind_index>(kind.index())));
187

188
        std::visit(
2,316✔
189
            [&](auto &kind) {
2,304✔
190
              // Print JSON values.
191
              PRINT_JSON_VALUE_IF_PRESENT(kind, name);
5,806✔
192
              if constexpr (btf_kind_traits<decltype(kind)>::has_linkage) {
193
                PRINT_JSON_FIXED("linkage",
222✔
194
                                 BTF_KIND_LINKAGE_TO_STRING(kind.linkage));
195
              }
196
              PRINT_JSON_VALUE_IF_PRESENT(kind, count_of_elements);
168✔
197
              PRINT_JSON_VALUE_IF_PRESENT(kind, size_in_bytes);
944✔
198
              PRINT_JSON_VALUE_IF_PRESENT(kind, size);
52✔
199
              PRINT_JSON_VALUE_IF_PRESENT(kind, is_struct);
40✔
200
              PRINT_JSON_VALUE_IF_PRESENT(kind, offset_from_start_in_bits);
818✔
201
              PRINT_JSON_VALUE_IF_PRESENT(kind, field_width_in_bits);
818✔
202
              PRINT_JSON_VALUE_IF_PRESENT(kind, is_signed);
828✔
203
              PRINT_JSON_VALUE_IF_PRESENT(kind, is_char);
818✔
204
              PRINT_JSON_VALUE_IF_PRESENT(kind, is_bool);
818✔
205

206
              // Print JSON arrays.
207
              if constexpr (btf_kind_traits<decltype(kind)>::has_members) {
208
                PRINT_JSON_ARRAY_START(kind, members);
261✔
209
                for (auto &member : kind.members) {
840✔
210
                  PRINT_JSON_OBJECT_START();
666✔
211
                  PRINT_JSON_VALUE_IF_PRESENT(member, name);
909✔
212
                  PRINT_JSON_VALUE_IF_PRESENT(member, value);
30✔
213
                  PRINT_JSON_VALUE_IF_PRESENT(member,
576✔
214
                                              offset_from_start_in_bits);
215
                  PRINT_JSON_VALUE_IF_PRESENT(member, offset);
60✔
216
                  PRINT_JSON_VALUE_IF_PRESENT(member, size);
60✔
217
                  PRINT_JSON_TYPE_IF_PRESENT(member, type);
2,466✔
218
                  PRINT_JSON_OBJECT_END();
666✔
219
                }
220
                PRINT_JSON_ARRAY_END();
87✔
221
              }
222

223
              if constexpr (btf_kind_traits<decltype(kind)>::has_parameters) {
224
                PRINT_JSON_ARRAY_START(kind, parameters);
132✔
225
                for (auto &parameter : kind.parameters) {
176✔
226
                  PRINT_JSON_OBJECT_START();
88✔
227
                  PRINT_JSON_VALUE_IF_PRESENT(parameter, name);
132✔
228
                  PRINT_JSON_TYPE_IF_PRESENT(parameter, type);
132✔
229
                  PRINT_JSON_OBJECT_END();
88✔
230
                }
231
                PRINT_JSON_ARRAY_END();
44✔
232
              }
233

234
              // Print JSON child object.
235
              PRINT_JSON_TYPE_IF_PRESENT(kind, type);
1,500✔
236
              PRINT_JSON_TYPE_IF_PRESENT(kind, element_type);
252✔
237
              PRINT_JSON_TYPE_IF_PRESENT(kind, index_type);
252✔
238
              PRINT_JSON_TYPE_IF_PRESENT(kind, return_type);
132✔
239
            },
2,304✔
240
            kind);
241
        PRINT_JSON_OBJECT_END();
2,316✔
242

243
        // scoped_visit automatically unmarks when it goes out of scope
244
      };
1,266✔
245

246
  // Determine the list of types that are not referenced by other types. These
247
  // are the root types.
248
  std::set<btf_type_id> root_types;
54✔
249

250
  // Add all types as root types.
251
  for (auto &[id, kind] : id_to_kind) {
1,476✔
252
    root_types.insert(id);
684✔
253
  }
254

255
  // Erase the VOID type.
256
  root_types.erase(0);
108✔
257

258
  // Remove all types that are referenced by other types.
259
  for (auto &[id, kind] : id_to_kind) {
1,476✔
260
    if (filter.has_value()) {
1,368✔
261
      if (!(*filter)(id)) {
×
262
        root_types.erase(id);
263
      }
264
      continue;
×
265
    }
266

267
    std::visit(
1,368✔
268
        [&](const auto &kind) {
728✔
269
          if constexpr (btf_kind_traits<decltype(kind)>::has_type) {
270
            root_types.erase(kind.type);
1,122✔
271
          }
272
          if constexpr (btf_kind_traits<decltype(kind)>::has_element_type) {
273
            root_types.erase(kind.element_type);
136✔
274
          }
275
          if constexpr (btf_kind_traits<decltype(kind)>::has_index_type) {
276
            root_types.erase(kind.index_type);
136✔
277
          }
278
          if constexpr (btf_kind_traits<decltype(kind)>::has_members) {
279
            for (auto &member : kind.members) {
744✔
280
              if constexpr (btf_kind_traits<decltype(member)>::has_type) {
281
                root_types.erase(member.type);
572✔
282
              }
283
            }
284
          }
285
          if constexpr (btf_kind_traits<decltype(kind)>::has_parameters) {
286
            for (auto &parameter : kind.parameters) {
176✔
287
              root_types.erase(parameter.type);
88✔
288
            }
289
          }
290
          if constexpr (btf_kind_traits<decltype(kind)>::has_return_type) {
291
            root_types.erase(kind.return_type);
88✔
292
          }
293
        },
728✔
294
        kind);
295
  }
296
  bool first = true;
54✔
297
  PRINT_JSON_OBJECT_START();
108✔
298
  PRINT_JSON_ARRAY_START("", btf_kinds);
162✔
299
  for (const auto &[id, kind] : id_to_kind) {
1,476✔
300
    // Skip non-root types.
301
    if (root_types.find(id) == root_types.end()) {
1,368✔
302
      continue;
1,200✔
303
    }
304

305
    out << (first ? "" : ",");
199✔
306
    first = false;
84✔
307
    print_btf_kind(id, kind);
252✔
308
  }
309
  PRINT_JSON_ARRAY_END();
54✔
310
  PRINT_JSON_OBJECT_END();
108✔
311
}
108✔
312

313
#pragma warning(pop)
314

315
} // namespace libbtf
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

© 2026 Coveralls, Inc