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

Alan-Jowett / libbtf / 15193410873

22 May 2025 05:48PM UTC coverage: 95.235% (-0.09%) from 95.324%
15193410873

Pull #145

github

web-flow
Merge e7e5ed437 into d675f7723
Pull Request #145: Remove loop detection in libbtf parser

1359 of 1427 relevant lines covered (95.23%)

1804.52 hits per line

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

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

4
#include "btf_type_data.h"
5

6
#include "btf.h"
7
#include "btf_json.h"
8
#include "btf_parse.h"
9
#include "btf_write.h"
10

11
#include <algorithm>
12
#include <iomanip>
13
#include <sstream>
14
#include <stdexcept>
15

16
namespace libbtf {
17

18
btf_type_data::btf_type_data(const std::vector<std::byte> &btf_data) {
142✔
19
  auto visitor = [&, this](btf_type_id id,
2,208✔
20
                           const std::optional<std::string> &name,
21
                           const btf_kind &kind) {
3,372✔
22
    this->id_to_kind.insert({id, kind});
2,208✔
23
    if (name.has_value()) {
2,208✔
24
      this->name_to_id.insert({name.value(), id});
1,164✔
25
    }
26
  };
2,208✔
27
  btf_parse_types(btf_data, visitor);
142✔
28
}
142✔
29

30
btf_type_id btf_type_data::get_id(const std::string &name) const {
110✔
31
  auto it = name_to_id.find(name);
55✔
32
  if (it == name_to_id.end()) {
110✔
33
    return 0;
22✔
34
  }
35
  return it->second;
66✔
36
}
37

38
btf_kind btf_type_data::get_kind(btf_type_id id) const {
10,036✔
39
  auto it = id_to_kind.find(id);
5,018✔
40
  if (it == id_to_kind.end()) {
10,036✔
41
    throw std::runtime_error("BTF type id not found: " + std::to_string(id));
3✔
42
  }
43
  return it->second;
15,051✔
44
}
45

46
btf_type_id btf_type_data::dereference_pointer(btf_type_id id) const {
82✔
47
  return get_kind_type<btf_kind_ptr>(id).type;
82✔
48
}
49

50
uint32_t btf_type_data::get_size(btf_type_id id) const {
966✔
51
  // Compute the effective size of a BTF type.
52
  return std::visit(
966✔
53
      [this, id](auto kind) -> uint32_t {
613✔
54
        if constexpr (std::is_same_v<decltype(kind), btf_kind_ptr>) {
55
          return sizeof(void *);
86✔
56
        } else if constexpr (btf_kind_traits<decltype(kind)>::has_type) {
57
          return get_size(kind.type);
190✔
58
        } else if constexpr (btf_kind_traits<decltype(
59
                                 kind)>::has_size_in_bytes) {
60
          return kind.size_in_bytes;
200✔
61
        } else if constexpr (std::is_same_v<decltype(kind), btf_kind_array>) {
62
          return kind.count_of_elements * get_size(kind.element_type);
70✔
63
        } else
64
          return 0;
67✔
65
      },
66
      get_kind(id));
1,932✔
67
}
68

69
void btf_type_data::to_json(
100✔
70
    std::ostream &out,
71
    std::optional<std::function<bool(btf_type_id)>> filter) const {
72
  btf_type_to_json(id_to_kind, out, filter);
100✔
73
}
100✔
74

75
std::vector<std::byte> btf_type_data::to_bytes() const {
50✔
76
  std::vector<btf_kind> kinds;
25✔
77
  for (const auto &[id, kind] : id_to_kind) {
722✔
78
    kinds.push_back(kind);
672✔
79
  }
80
  return btf_write_types(kinds);
100✔
81
}
50✔
82

83
void btf_type_data::replace(btf_type_id id, const btf_kind &kind) {
2✔
84
  if (id_to_kind.find(id) == id_to_kind.end()) {
2✔
85
    throw std::runtime_error("BTF type not found: " + std::to_string(id));
×
86
  }
87

88
  id_to_kind[id] = kind;
2✔
89
  update_name_to_id(id);
2✔
90
}
2✔
91

92
btf_type_id btf_type_data::append(const btf_kind &kind) {
82✔
93
  if (id_to_kind.size() > UINT32_MAX) {
82✔
94
    throw std::runtime_error("Too many BTF types");
×
95
  }
96
  btf_type_id next_id = static_cast<btf_type_id>(id_to_kind.size());
82✔
97
  id_to_kind.insert({next_id, kind});
82✔
98
  update_name_to_id(next_id);
82✔
99
  return next_id;
82✔
100
}
101

102
void btf_type_data::update_name_to_id(btf_type_id id) {
84✔
103

104
  auto name = get_type_name(id);
84✔
105
  if (!name.empty()) {
84✔
106
    name_to_id.insert({name, id});
28✔
107
  }
108
}
84✔
109

110
void btf_type_data::visit_depth_first(
4,384✔
111
    std::optional<std::function<bool(btf_type_id)>> before,
112
    std::optional<std::function<void(btf_type_id)>> after,
113
    btf_type_id id) const {
114
  if (before) {
4,384✔
115
    if (!(*before)(id)) {
4,384✔
116
      return;
117
    }
118
  }
119

120
  std::visit(
4,384✔
121
      [&, this](auto kind) {
4,422✔
122
        if constexpr (btf_kind_traits<decltype(kind)>::has_type) {
123
          visit_depth_first(before, after, kind.type);
6,489✔
124
        }
125
        if constexpr (btf_kind_traits<decltype(kind)>::has_index_type) {
126
          visit_depth_first(before, after, kind.index_type);
812✔
127
        }
128
        if constexpr (btf_kind_traits<decltype(kind)>::has_element_type) {
129
          visit_depth_first(before, after, kind.element_type);
812✔
130
        }
131
        if constexpr (btf_kind_traits<decltype(kind)>::has_members) {
132
          for (auto member : kind.members) {
1,256✔
133
            if constexpr (btf_kind_traits<decltype(member)>::has_type) {
134
              visit_depth_first(before, after, member.type);
1,563✔
135
            }
136
          }
137
        }
138
        if constexpr (btf_kind_traits<decltype(kind)>::has_return_type) {
139
          visit_depth_first(before, after, kind.return_type);
176✔
140
        }
141
        if constexpr (btf_kind_traits<decltype(kind)>::has_parameters) {
142
          for (auto param : kind.parameters) {
176✔
143
            if constexpr (btf_kind_traits<decltype(param)>::has_type) {
144
              visit_depth_first(before, after, param.type);
132✔
145
            }
146
          }
147
        }
148
      },
3,407✔
149
      get_kind(id));
8,768✔
150

151
  if (after) {
4,384✔
152
    (*after)(id);
×
153
  }
154
}
155

156
std::vector<btf_type_id> btf_type_data::dependency_order(
40✔
157
    std::optional<std::function<bool(btf_type_id)>> filter) const {
158
  std::map<btf_type_id, std::set<btf_type_id>> children;
20✔
159
  std::map<btf_type_id, std::set<btf_type_id>> parents;
20✔
160
  std::set<btf_type_id> filtered_types;
20✔
161
  std::vector<btf_type_id> result;
20✔
162

163
  // Build list of dependencies.
164
  for (const auto &[id, kind] : id_to_kind) {
672✔
165
    // Copy id to a local variable to workaround a bug in Apple's clang.
166
    // See: https://github.com/llvm/llvm-project/issues/48582
167
    auto local_id = id;
632✔
168
    bool match = false;
632✔
169
    if (!filter || (*filter)(local_id)) {
632✔
170
      match = true;
632✔
171
    }
172
    auto pre = [&](btf_type_id visit_id) -> bool {
4,384✔
173
      if (match) {
4,384✔
174
        filtered_types.insert(visit_id);
4,384✔
175
      }
176
      if (visit_id != local_id) {
6,260✔
177
        children[local_id].insert(visit_id);
3,752✔
178
        parents[visit_id].insert(local_id);
4,068✔
179
      } else {
180
        parents.insert({local_id, {}});
632✔
181
        children.insert({local_id, {}});
632✔
182
      }
183
      return true;
4,384✔
184
    };
632✔
185

186
    visit_depth_first(pre, std::nullopt, local_id);
948✔
187
  }
188

189
  while (!parents.empty()) {
302✔
190
    std::vector<btf_type_id> types_to_remove;
131✔
191
    // Find all types with no parents.
192
    for (auto &[id, child_set] : parents) {
2,764✔
193
      if (child_set.empty()) {
2,502✔
194
        types_to_remove.push_back(id);
632✔
195
      }
196
    }
197

198
    // Remove these parents from all children.
199
    for (auto id : types_to_remove) {
894✔
200
      for (auto child : children[id]) {
2,804✔
201
        parents[child].erase(id);
2,172✔
202
      }
203
      parents.erase(id);
316✔
204
    }
205
    // Append these types to the result.
206
    result.insert(result.end(), types_to_remove.begin(), types_to_remove.end());
262✔
207
  }
131✔
208

209
  // Remove types that are not children of the filtered type.
210
  std::vector<btf_type_id> filtered_result;
20✔
211
  for (auto id : result) {
672✔
212
    if (filtered_types.find(id) != filtered_types.end()) {
632✔
213
      filtered_result.push_back(id);
632✔
214
    }
215
  }
216

217
  std::reverse(filtered_result.begin(), filtered_result.end());
20✔
218
  return filtered_result;
60✔
219
}
20✔
220

221
void btf_type_data::to_c_header(
40✔
222
    std::ostream &out,
223
    std::optional<std::function<bool(btf_type_id)>> filter) const {
224
  std::set<btf_type_id> declared_types;
20✔
225

226
  size_t indent = 0;
40✔
227
  out << "#pragma once\n\n";
40✔
228

229
  // Print each type in dependency order.
230
  for (auto id : dependency_order(filter)) {
692✔
231
    if (get_type_name(id).empty()) {
948✔
232
      continue;
334✔
233
    }
234
    std::visit(
298✔
235
        [&, this](auto kind) {
339✔
236
          if constexpr (std::is_same_v<decltype(kind), btf_kind_typedef>) {
237
            out << "typedef ";
168✔
238
            out << get_type_declaration(kind.type, kind.name, indent)
148✔
239
                << ";\n\n";
93✔
240
          } else if constexpr (std::is_same_v<decltype(kind),
241
                                              btf_kind_struct>) {
242
            out << get_type_declaration(id, "", indent) << ";\n\n";
24✔
243
          } else if constexpr (std::is_same_v<decltype(kind), btf_kind_union>) {
244
            out << get_type_declaration(id, "", indent) << ";\n\n";
×
245
          } else if constexpr (std::is_same_v<decltype(kind), btf_kind_fwd>) {
246
            out << (kind.is_struct ? "union" : "struct ") << kind.name
30✔
247
                << ";\n\n";
40✔
248
          } else if constexpr (std::is_same_v<decltype(kind), btf_kind_var>) {
249
            out << get_type_declaration(kind.type, kind.name, indent)
30✔
250
                << ";\n\n";
45✔
251
          } else if constexpr (std::is_same_v<decltype(kind),
252
                                              btf_kind_function>) {
253
            if (kind.linkage == BTF_LINKAGE_STATIC) {
44✔
254
              out << "static ";
×
255
            } else if (kind.linkage == BTF_LINKAGE_EXTERN) {
44✔
256
              out << "extern ";
×
257
            }
258
            out << get_type_declaration(kind.type, kind.name, indent)
44✔
259
                << ";\n\n";
66✔
260
          }
261
        },
233✔
262
        get_kind(id));
596✔
263
  }
20✔
264
}
40✔
265

266
std::string btf_type_data::get_type_name(btf_type_id id) const {
1,188✔
267
  // Use visit to return the name if the type has it.
268
  auto kind = get_kind(id);
1,188✔
269
  return std::visit(
270
      [](auto kind) -> std::string {
594✔
271
        if constexpr (btf_kind_traits<decltype(kind)>::has_optional_name) {
272
          return kind.name.value_or("");
60✔
273
        } else if constexpr (btf_kind_traits<decltype(kind)>::has_name) {
274
          return kind.name;
628✔
275
        } else {
276
          return "";
500✔
277
        }
278
      },
279
      get_kind(id));
2,970✔
280
}
594✔
281

282
std::string btf_type_data::get_qualified_type_name(btf_type_id id) const {
132✔
283
  // Use visit to return the name if the type has it.
284
  auto kind = get_kind(id);
132✔
285
  return std::visit(
286
      [this](auto kind) -> std::string {
154✔
287
        // Add possible qualifiers.
288
        std::string qualifier;
66✔
289
        if constexpr (std::is_same_v<decltype(kind), btf_kind_const>) {
290
          qualifier = "const ";
291
        } else if constexpr (std::is_same_v<decltype(kind),
292
                                            btf_kind_volatile>) {
293
          qualifier = "volatile ";
294
        } else if constexpr (std::is_same_v<decltype(kind),
295
                                            btf_kind_restrict>) {
296
          qualifier = "restrict ";
297
        }
298

299
        std::string suffix;
66✔
300
        if constexpr (std::is_same_v<decltype(kind), btf_kind_ptr>) {
301
          suffix = "*";
22✔
302
        }
303

304
        if constexpr (btf_kind_traits<decltype(kind)>::has_optional_name) {
305
          return qualifier + kind.name.value_or("") + suffix;
40✔
306
        } else if constexpr (btf_kind_traits<decltype(kind)>::has_name) {
307
          return kind.name + suffix;
120✔
308
        } else if constexpr (btf_kind_traits<decltype(kind)>::has_type) {
309
          return qualifier + this->get_qualified_type_name(kind.type) + suffix;
110✔
310
        } else if constexpr (std::is_same_v<decltype(kind), btf_kind_void>) {
311
          return qualifier + "void" + suffix;
30✔
312
        } else {
313
          return "";
×
314
        }
315
      },
66✔
316
      get_kind(id));
330✔
317
}
66✔
318

319
btf_type_id btf_type_data::get_descendant_type_id(btf_type_id id) const {
×
320
  // Get the type id lowest in the tree.
321
  auto kind = get_kind(id);
×
322
  return std::visit(
×
323
      [id, this](auto kind) -> btf_type_id {
×
324
        if constexpr (btf_kind_traits<decltype(kind)>::has_type) {
325
          return this->get_descendant_type_id(kind.type);
×
326
        } else {
327
          return id;
×
328
        }
329
      },
330
      kind);
×
331
}
332

333
std::string btf_type_data::get_type_declaration(btf_type_id id,
442✔
334
                                                const std::string &name,
335
                                                size_t indent) const {
336
  // Build a string of type qualifiers.
337
  std::string result = std::string(indent, ' ');
442✔
338
  auto kind = get_kind(id);
442✔
339
  std::visit(
442✔
340
      [&](auto kind) {
442✔
341
        if constexpr (std::is_same_v<decltype(kind), btf_kind_typedef>) {
342
          result += get_type_name(id) + " " + name;
1,649✔
343
        } else if constexpr (std::is_same_v<decltype(kind), btf_kind_array>) {
344
          auto local_name = name;
42✔
345
          if (!local_name.empty() && local_name[0] == '*') {
84✔
346
            local_name = "(" + local_name + ")";
111✔
347
          }
348
          auto local_type = get_type_name(kind.element_type);
84✔
349
          if (local_type.empty()) {
84✔
350
            local_type = get_type_declaration(kind.element_type, "", indent);
460✔
351
          }
352
          result += local_type + " " + local_name + "[" +
126✔
353
                    std::to_string(kind.count_of_elements) + "]";
354
        } else
42✔
355
            // If kind is btf_kind_const, add const
356
            if constexpr (std::is_same_v<decltype(kind), btf_kind_const>) {
357
          result += "const " + get_type_declaration(kind.type, name, indent);
×
358
        } else
359
            // If kind is btf_kind_volatile, add volatile
360
            if constexpr (std::is_same_v<decltype(kind), btf_kind_volatile>) {
361
          result += "volatile " + get_type_declaration(kind.type, name, indent);
×
362
        } else
363
            // If kind is btf_kind_restrict, add restrict
364
            if constexpr (std::is_same_v<decltype(kind), btf_kind_restrict>) {
365
          result += "restrict " + get_type_declaration(kind.type, name, indent);
×
366
        } else
367
            // If kind is btf_kind_ptr, add *
368
            if constexpr (std::is_same_v<decltype(kind), btf_kind_ptr>) {
369
          result = get_type_declaration(kind.type, "*" + name, indent);
210✔
370
        } else if constexpr (std::is_same_v<decltype(kind), btf_kind_struct>) {
371
          if (kind.name.has_value()) {
46✔
372
            result = "struct " + kind.name.value_or("") + " {\n";
18✔
373
          } else {
374
            result = "struct {\n";
17✔
375
          }
376
          for (auto member : kind.members) {
306✔
377
            std::string type_name = get_type_name(member.type);
260✔
378
            if (type_name.empty()) {
260✔
379
              result += get_type_declaration(
219✔
380
                            member.type, member.name.value_or(""), indent + 2) +
381
                        ";\n";
382
            } else {
383
              result += std::string(indent + 2, ' ') + type_name + " " +
171✔
384
                        member.name.value_or("") + ";\n";
385
            }
386
          }
387
          result += std::string(indent, ' ') + "}";
69✔
388
          if (!name.empty()) {
46✔
389
            result += " " + name;
51✔
390
          }
391
        } else if constexpr (std::is_same_v<decltype(kind), btf_kind_union>) {
392
          if (kind.name.has_value()) {
4✔
393
            result += "union " + kind.name.value_or("") + " {\n";
×
394
          } else {
395
            result += "union {\n";
2✔
396
          }
397
          for (auto member : kind.members) {
8✔
398
            std::string type_name = get_type_name(member.type);
4✔
399
            if (type_name.empty()) {
4✔
400
              result += get_type_declaration(
6✔
401
                            member.type, member.name.value_or(""), indent + 2) +
402
                        ";\n";
403
            } else {
404
              result += std::string(indent + 2, ' ') + type_name + " " +
×
405
                        member.name.value_or("") + ";\n";
406
            }
407
          }
408
          result += std::string(indent, ' ') + "}";
6✔
409
          if (!name.empty()) {
4✔
410
            result += " " + name;
×
411
          }
412
        } else if constexpr (std::is_same_v<decltype(kind),
413
                                            btf_kind_function_prototype>) {
414
          result +=
66✔
415
              get_qualified_type_name(kind.return_type) + " " + name + "(";
416
          for (auto param : kind.parameters) {
88✔
417
            result += get_qualified_type_name(param.type);
66✔
418
            if (!param.name.empty()) {
44✔
419
              result += " " + param.name;
66✔
420
            }
421
            result += ", ";
22✔
422
          }
423
          if (kind.parameters.size() > 0) {
44✔
424
            result.pop_back();
22✔
425
            result.pop_back();
22✔
426
          }
427
          result += ")";
22✔
428
        } else if constexpr (!btf_kind_traits<decltype(kind)>::has_type) {
429
          result += get_type_name(id) + " " + name;
84✔
430
        }
431
      },
442✔
432
      kind);
433

434
  return result;
663✔
435
}
221✔
436
} // 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