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

Alan-Jowett / libbtf / 18665245266

20 Oct 2025 09:20PM UTC coverage: 96.12% (+0.8%) from 95.331%
18665245266

Pull #173

github

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

868 of 911 new or added lines in 6 files covered. (95.28%)

11 existing lines in 3 files now uncovered.

2180 of 2268 relevant lines covered (96.12%)

1549.88 hits per line

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

94.24
/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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

155
void btf_type_to_json(const std::map<btf_type_id, btf_kind> &id_to_kind,
108✔
156
                      std::ostream &out,
157
                      std::optional<std::function<bool(btf_type_id)>> filter) {
158
  std::set<btf_type_id> currently_printing; // Track cycles
54✔
159

160
  std::function<void(btf_type_id, const btf_kind &)> print_btf_kind =
161
      [&](btf_type_id id, const btf_kind &kind) {
2,316✔
162
        // Check for cycles
163
        if (currently_printing.find(id) != currently_printing.end()) {
4,632✔
164
          // We're in a cycle - print just a reference
165
          bool first = true;
NEW
166
          PRINT_JSON_OBJECT_START();
×
NEW
167
          PRINT_JSON_FIXED("id", id);
×
NEW
168
          PRINT_JSON_FIXED("kind_type", "reference");
×
NEW
169
          PRINT_JSON_OBJECT_END();
×
NEW
170
          return;
×
171
        }
172

173
        currently_printing.insert(id);
1,158✔
174

175
        bool first = true;
1,158✔
176
        PRINT_JSON_OBJECT_START();
2,316✔
177
        PRINT_JSON_FIXED("id", id);
3,474✔
178
        PRINT_JSON_FIXED("kind_type",
2,316✔
179
                         BTF_KIND_INDEX_TO_STRING(
180
                             static_cast<btf_kind_index>(kind.index())));
181

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

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

217
              if constexpr (btf_kind_traits<decltype(kind)>::has_parameters) {
218
                PRINT_JSON_ARRAY_START(kind, parameters);
132✔
219
                for (auto &parameter : kind.parameters) {
176✔
220
                  PRINT_JSON_OBJECT_START();
88✔
221
                  PRINT_JSON_VALUE_IF_PRESENT(parameter, name);
132✔
222
                  PRINT_JSON_TYPE_IF_PRESENT(parameter, type);
132✔
223
                  PRINT_JSON_OBJECT_END();
88✔
224
                }
225
                PRINT_JSON_ARRAY_END();
44✔
226
              }
227

228
              // Print JSON child object.
229
              PRINT_JSON_TYPE_IF_PRESENT(kind, type);
1,500✔
230
              PRINT_JSON_TYPE_IF_PRESENT(kind, element_type);
252✔
231
              PRINT_JSON_TYPE_IF_PRESENT(kind, index_type);
252✔
232
              PRINT_JSON_TYPE_IF_PRESENT(kind, return_type);
132✔
233
            },
2,304✔
234
            kind);
235
        PRINT_JSON_OBJECT_END();
2,316✔
236

237
        currently_printing.erase(id);
1,158✔
238
      };
108✔
239

240
  // Determine the list of types that are not referenced by other types. These
241
  // are the root types.
242
  std::set<btf_type_id> root_types;
54✔
243

244
  // Add all types as root types.
245
  for (auto &[id, kind] : id_to_kind) {
1,476✔
246
    root_types.insert(id);
684✔
247
  }
248

249
  // Erase the VOID type.
250
  root_types.erase(0);
108✔
251

252
  // Remove all types that are referenced by other types.
253
  for (auto &[id, kind] : id_to_kind) {
1,476✔
254
    if (filter.has_value()) {
1,368✔
UNCOV
255
      if (!(*filter)(id)) {
×
256
        root_types.erase(id);
257
      }
UNCOV
258
      continue;
×
259
    }
260

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

299
    out << (first ? "" : ",");
199✔
300
    first = false;
84✔
301
    print_btf_kind(id, kind);
252✔
302
  }
303
  PRINT_JSON_ARRAY_END();
54✔
304
  PRINT_JSON_OBJECT_END();
108✔
305
}
108✔
306

307
#pragma warning(pop)
308

309
} // 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