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

Alan-Jowett / libbtf / 15799657858

04 Jun 2025 03:15PM UTC coverage: 93.874% (-1.5%) from 95.328%
15799657858

push

github

web-flow
Bump the actions group across 1 directory with 2 updates (#147)

Bumps the actions group with 2 updates in the / directory: [github/codeql-action](https://github.com/github/codeql-action) and [ossf/scorecard-action](https://github.com/ossf/scorecard-action).


Updates `github/codeql-action` from 3.28.18 to 3.28.19
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/github/codeql-action/compare/ff0a06e83...fca7ace96)

Updates `ossf/scorecard-action` from 2.4.1 to 2.4.2
- [Release notes](https://github.com/ossf/scorecard-action/releases)
- [Changelog](https://github.com/ossf/scorecard-action/blob/main/RELEASE.md)
- [Commits](https://github.com/ossf/scorecard-action/compare/f49aabe0b...05b42c624)

---
updated-dependencies:
- dependency-name: github/codeql-action
  dependency-version: 3.28.19
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: actions
- dependency-name: ossf/scorecard-action
  dependency-version: 2.4.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: actions
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

1134 of 1208 relevant lines covered (93.87%)

1213.75 hits per line

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

98.42
/test/test.cpp
1
// Copyright (c) Prevail Verifier contributors.
2
// SPDX-License-Identifier: MIT
3
#include <catch2/catch_all.hpp>
4

5
#include <map>
6
#include <regex>
7
#include <string>
8
#include <vector>
9

10
#if !defined(MAX_PATH)
11
#define MAX_PATH (256)
12
#endif
13

14
#include "btf.h"
15
#include "btf_json.h"
16
#include "btf_map.h"
17
#include "btf_parse.h"
18
#include "btf_type_data.h"
19
#include "btf_write.h"
20

21
// Suppress some W4 warnings from the elfio library.
22
#pragma warning(push)
23
#pragma warning(disable : 4244)
24
#pragma warning(disable : 4458)
25
#include "elfio/elfio.hpp"
26
#pragma warning(pop)
27

28
std::map<std::string, std::string> string_replacements = {
29
    // ebpf-samples JSON incorrectly swapped static and global.
30
    {"BTF_LINKAGE_STATIC", "BTF_LINKAGE_GLOBAL"},
31
    {"BTF_LINKAGE_GLOBAL", "BTF_LINKAGE_STATIC"},
32
    // BTF_KIND_FUNC renamed to BTF_KIND_FUNCTION
33
    {"BTF_KIND_FUNC", "BTF_KIND_FUNCTION"},
34
    // BTF_KIND_FUNC renamed to BTF_KIND_FUNCTION
35
    {"BTF_KIND_FUNCTION_PROTO", "BTF_KIND_FUNCTION_PROTOTYPE"},
36
    // BTF_FUNC_GLOBAL renamed to BTF_LINKAGE_GLOBAL
37
    {"BTF_FUNC_GLOBAL", "BTF_LINKAGE_GLOBAL"},
38
    // BTF_KIND_DATASEC renamed to BTF_KIND_DATA_SECTION
39
    {"BTF_KIND_DATASEC", "BTF_KIND_DATA_SECTION"},
40
};
41

42
#define TEST_OBJECT_FILE_DIRECTORY "external/ebpf-samples/build/"
43
#define TEST_SOURCE_FILE_DIRECTORY "external/ebpf-samples/src/"
44
#define TEST_JSON_FILE_DIRECTORY "json/"
45
#define TEST_C_HEADER_FILE_DIRECTORY "test/expected/"
46
#define BTF_CASE(file, apply_replacements)                                     \
47
  TEST_CASE("BTF JSON suite: " file, "[json]") {                               \
48
    verify_BTF_json(file, apply_replacements);                                 \
49
  }                                                                            \
50
  TEST_CASE("BTF LINE_INFO suite: " file, "[line_info]") {                     \
51
    verify_line_info(file);                                                    \
52
  }                                                                            \
53
  TEST_CASE("BTF C header suite: " file, "[c_header]") {                       \
54
    verify_c_header(file);                                                     \
55
  }
56

57
void verify_line_by_line(std::istream &lhs, std::istream &rhs,
45✔
58
                         bool apply_replacements = false) {
59
  std::string lhs_line;
60
  std::string rhs_line;
61
  while (std::getline(lhs, lhs_line)) {
5,412✔
62
    bool has_more = (bool)std::getline(rhs, rhs_line);
5,367✔
63
    REQUIRE(has_more);
5,367✔
64
    if (apply_replacements) {
5,367✔
65
      for (const auto &[old_string, new_string] : string_replacements) {
28,854✔
66
        lhs_line =
67
            std::regex_replace(lhs_line, std::regex(old_string), new_string);
49,464✔
68
      }
69
    }
70
    REQUIRE(lhs_line == rhs_line);
10,734✔
71
  }
72
  bool has_more = (bool)std::getline(rhs, rhs_line);
45✔
73
  REQUIRE_FALSE(has_more);
45✔
74
}
45✔
75

76
void verify_BTF_json(const std::string &file, bool apply_replacements = true) {
20✔
77
  std::stringstream generated_output;
20✔
78
  auto reader = ELFIO::elfio();
20✔
79
  REQUIRE(reader.load(std::string(TEST_OBJECT_FILE_DIRECTORY) + file + ".o"));
40✔
80

81
  auto btf = reader.sections[".BTF"];
20✔
82

83
  libbtf::btf_type_data btf_data = std::vector<std::byte>(
40✔
84
      {reinterpret_cast<const std::byte *>(btf->get_data()),
20✔
85
       reinterpret_cast<const std::byte *>(btf->get_data() + btf->get_size())});
20✔
86

87
  // Get the size of each type.
88
  for (libbtf::btf_type_id id = 1; id <= btf_data.last_type_id(); id++) {
316✔
89
    size_t size = btf_data.get_size(id);
296✔
90
    switch (btf_data.get_kind_index(id)) {
296✔
91
    // Non-zero sized types.
92
    case libbtf::BTF_KIND_INT:
164✔
93
    case libbtf::BTF_KIND_PTR:
94
    case libbtf::BTF_KIND_STRUCT:
95
    case libbtf::BTF_KIND_UNION:
96
    case libbtf::BTF_KIND_ENUM:
97
    case libbtf::BTF_KIND_VAR:
98
    case libbtf::BTF_KIND_FLOAT:
99
    case libbtf::BTF_KIND_ENUM64:
100
      REQUIRE(size != 0);
328✔
101
      break;
164✔
102
    case libbtf::BTF_KIND_FWD:
67✔
103
    case libbtf::BTF_KIND_VOLATILE:
104
    case libbtf::BTF_KIND_CONST:
105
    case libbtf::BTF_KIND_RESTRICT:
106
    case libbtf::BTF_KIND_FUNCTION:
107
    case libbtf::BTF_KIND_FUNCTION_PROTOTYPE:
108
    case libbtf::BTF_KIND_DATA_SECTION:
109
    case libbtf::BTF_KIND_DECL_TAG:
110
    case libbtf::BTF_KIND_TYPE_TAG:
111
      REQUIRE(size == 0);
134✔
112
      break;
67✔
113
    // Array can be zero sized if there are no elements.
114
    case libbtf::BTF_KIND_ARRAY: {
34✔
115
      auto array = btf_data.get_kind_type<libbtf::btf_kind_array>(id);
34✔
116
      REQUIRE(size ==
68✔
117
              array.count_of_elements * btf_data.get_size(array.element_type));
118
      break;
119
    }
120
    }
121
  }
122

123
  btf_data.to_json(generated_output);
40✔
124

125
  // Pretty print the JSON output.
126
  std::string pretty_printed_json =
127
      libbtf::pretty_print_json(generated_output.str());
20✔
128

129
  // Read the expected output from the .json file.
130
  std::ifstream expected_stream(std::string(TEST_JSON_FILE_DIRECTORY) + file +
40✔
131
                                std::string(".json"));
60✔
132
  std::stringstream generated_stream(pretty_printed_json);
40✔
133

134
  verify_line_by_line(expected_stream, generated_stream, apply_replacements);
20✔
135

136
  // Verify that encoding the BTF data and parsing it again results in the same
137
  // JSON.
138
  libbtf::btf_type_data btf_data_round_trip = btf_data.to_bytes();
40✔
139

140
  std::stringstream generated_output_round_trip;
40✔
141
  btf_data_round_trip.to_json(generated_output_round_trip);
40✔
142

143
  // Pretty print the JSON output.
144
  std::string pretty_printed_json_round_trip =
145
      libbtf::pretty_print_json(generated_output_round_trip.str());
20✔
146

147
  // Verify that the pretty printed JSON is the same as the original.
148
  REQUIRE(pretty_printed_json == pretty_printed_json_round_trip);
40✔
149
}
20✔
150

151
void verify_line_info(const std::string &file) {
20✔
152
  auto reader = ELFIO::elfio();
20✔
153
  REQUIRE(reader.load(std::string(TEST_OBJECT_FILE_DIRECTORY) + file + ".o"));
40✔
154

155
  auto btf_section = reader.sections[".BTF"];
20✔
156
  auto btfext_section = reader.sections[".BTF.ext"];
20✔
157

158
  std::vector<std::byte> btf = {
159
      reinterpret_cast<const std::byte *>(btf_section->get_data()),
20✔
160
      reinterpret_cast<const std::byte *>(btf_section->get_data() +
20✔
161
                                          btf_section->get_size())};
20✔
162
  std::vector<std::byte> btfext = {
163
      reinterpret_cast<const std::byte *>(btfext_section->get_data()),
20✔
164
      reinterpret_cast<const std::byte *>(btfext_section->get_data() +
20✔
165
                                          btfext_section->get_size())};
20✔
166

167
  // Read source file into array of strings.
168
  std::vector<std::string> source_lines;
169
  std::ifstream source_file(std::string(TEST_SOURCE_FILE_DIRECTORY) + file +
40✔
170
                            ".c");
20✔
171

172
  std::string line;
173
  while (std::getline(source_file, line)) {
566✔
174
    // Strip any trailing whitespace.
175
    line = line.substr(0, line.find_last_not_of(" \t\n\r\f\v") + 1);
546✔
176
    source_lines.push_back(line);
546✔
177
  }
178

179
  libbtf::btf_parse_line_information(
40✔
180
      btf, btfext,
181
      [&](const std::string &section, uint32_t instruction_offset,
201✔
182
          const std::string &file_name, const std::string &source,
183
          uint32_t line_number, uint32_t column_number) {
184
        // column_number is not used.
185
        (void)column_number;
186
        // instruction_offset is not used.
187
        (void)instruction_offset;
188
        if (!source.empty()) {
201✔
189
          // Removing any trailing whitespace.
190
          std::string stripped_source =
191
              source.substr(0, source.find_last_not_of(" \t\n\r\f\v") + 1);
185✔
192

193
          // Verify that the source line is correct.
194
          REQUIRE(stripped_source == source_lines[line_number - 1]);
370✔
195
          REQUIRE(line_number > 0);
370✔
196

197
          // Verify that the file_name matches the source file with the path
198
          // removed.
199
          std::string stripped_file_name =
200
              file_name.substr(file_name.find_last_of("/\\") + 1);
185✔
201
          REQUIRE(stripped_file_name == file + ".c");
413✔
202

203
          // Verify that the section name is present in the ELF file.
204
          REQUIRE(reader.sections[section] != nullptr);
370✔
205
        }
206
      });
201✔
207
}
40✔
208

209
void verify_c_header(const std::string &file) {
20✔
210
  std::stringstream generated_output;
20✔
211
  auto reader = ELFIO::elfio();
20✔
212
  REQUIRE(reader.load(std::string(TEST_OBJECT_FILE_DIRECTORY) + file + ".o"));
40✔
213

214
  auto btf = reader.sections[".BTF"];
20✔
215

216
  libbtf::btf_type_data btf_data = std::vector<std::byte>(
20✔
217
      {reinterpret_cast<const std::byte *>(btf->get_data()),
20✔
218
       reinterpret_cast<const std::byte *>(btf->get_data() + btf->get_size())});
20✔
219

220
  btf_data.to_c_header(generated_output);
20✔
221

222
  // Read the expected output from the .h file.
223
  std::ifstream expected_stream(std::string(TEST_C_HEADER_FILE_DIRECTORY) +
40✔
224
                                file + std::string(".h"));
80✔
225

226
  // Skip over the first two line of the header file, which is a comment
227
  // containing the name of ELF file and the name of the header file.
228
  std::string line;
229
  std::getline(expected_stream, line);
20✔
230
  std::getline(expected_stream, line);
20✔
231

232
  verify_line_by_line(expected_stream, generated_output);
20✔
233
}
20✔
234

235
BTF_CASE("byteswap", true)
6✔
236
BTF_CASE("ctxoffset", true)
6✔
237
BTF_CASE("exposeptr", true)
6✔
238
BTF_CASE("exposeptr2", true)
6✔
239
BTF_CASE("map_in_map", false)
6✔
240
BTF_CASE("map_in_map_anonymous", false)
6✔
241
BTF_CASE("mapoverflow", true)
6✔
242
BTF_CASE("mapunderflow", true)
6✔
243
BTF_CASE("mapvalue-overrun", true)
6✔
244
BTF_CASE("nullmapref", true)
6✔
245
BTF_CASE("packet_access", true)
6✔
246
BTF_CASE("packet_overflow", true)
6✔
247
BTF_CASE("packet_reallocate", true)
6✔
248
BTF_CASE("packet_start_ok", true)
6✔
249
BTF_CASE("stackok", true)
6✔
250
BTF_CASE("tail_call", true)
6✔
251
BTF_CASE("tail_call_bad", true)
6✔
252
BTF_CASE("twomaps", true)
6✔
253
BTF_CASE("twostackvars", true)
6✔
254
BTF_CASE("twotypes", true)
6✔
255

256
TEST_CASE("validate-parsing-simple-loop", "[validation]") {
1✔
257
  libbtf::btf_type_data btf_data_loop;
1✔
258
  btf_data_loop.append(libbtf::btf_kind_ptr{.type = 1});
1✔
259

260
  REQUIRE_THROWS(
2✔
261
      [&] { libbtf::btf_type_data btf_data = btf_data_loop.to_bytes(); }());
262
}
1✔
263

264
TEST_CASE("validate-parsing-large-loop", "[validation]") {
1✔
265
  libbtf::btf_type_data btf_data_loop;
1✔
266

267
  // Each PTR points to the next PTR.
268
  for (uint32_t i = 0; i < 10; i++) {
11✔
269
    btf_data_loop.append(libbtf::btf_kind_ptr{.type = i + 1});
20✔
270
  }
271
  // Last PTR points to itself.
272
  btf_data_loop.append(libbtf::btf_kind_ptr{.type = 1});
1✔
273

274
  REQUIRE_THROWS(
2✔
275
      [&] { libbtf::btf_type_data btf_data = btf_data_loop.to_bytes(); }());
276
}
1✔
277

278
TEST_CASE("enum_type", "[parsing][json]") {
1✔
279
  libbtf::btf_type_data btf_data;
1✔
280
  btf_data.append(libbtf::btf_kind_enum{.name = "enum_type",
7✔
281
                                        .members = {
282
                                            {.name = "A", .value = 0},
283
                                            {.name = "B", .value = 1},
284
                                            {.name = "C", .value = 2},
285
                                        }});
3✔
286

287
  libbtf::btf_type_data btf_data_round_trip = btf_data.to_bytes();
1✔
288
  std::stringstream generated_output;
1✔
289
  btf_data.to_json(generated_output);
1✔
290

291
  std::stringstream generated_output_round_trip;
1✔
292
  btf_data_round_trip.to_json(generated_output_round_trip);
1✔
293

294
  std::string expected_json = "{\n"
295
                              "  \"btf_kinds\": [\n"
296
                              "    {\n"
297
                              "      \"id\": 1,\n"
298
                              "      \"kind_type\": \"BTF_KIND_ENUM\",\n"
299
                              "      \"name\": \"enum_type\",\n"
300
                              "      \"size_in_bytes\": 0,\n"
301
                              "      \"is_signed\": false,\n"
302
                              "      \"members\": [\n"
303
                              "        {\n"
304
                              "          \"name\": \"A\",\n"
305
                              "          \"value\": 0\n"
306
                              "        },\n"
307
                              "        {\n"
308
                              "          \"name\": \"B\",\n"
309
                              "          \"value\": 1\n"
310
                              "        },\n"
311
                              "        {\n"
312
                              "          \"name\": \"C\",\n"
313
                              "          \"value\": 2\n"
314
                              "        }\n"
315
                              "      ]\n"
316
                              "    }\n"
317
                              "  ]\n"
318
                              "}\n";
1✔
319
  std::string pretty_printed_generated_output =
320
      libbtf::pretty_print_json(generated_output.str());
1✔
321

322
  REQUIRE(generated_output.str() == generated_output_round_trip.str());
5✔
323

324
  std::stringstream generated_json_stream(pretty_printed_generated_output);
1✔
325
  std::stringstream expected_json_stream(expected_json);
1✔
326
  verify_line_by_line(generated_json_stream, expected_json_stream);
1✔
327

328
  // Compare the generated JSON to the round trip JSON.
329
  REQUIRE(generated_output.str() == generated_output_round_trip.str());
5✔
330
}
2✔
331

332
TEST_CASE("enum64_type", "[parsing][json]") {
1✔
333
  libbtf::btf_type_data btf_data;
1✔
334
  btf_data.append(
2✔
335
      libbtf::btf_kind_enum64{.name = "enum_type",
5✔
336
                              .members = {
337
                                  {.name = "A", .value = UINT64_MAX - 100},
338
                                  {.name = "B", .value = UINT64_MAX - 99},
339
                                  {.name = "C", .value = UINT64_MAX - 98},
340
                              }});
3✔
341

342
  libbtf::btf_type_data btf_data_round_trip = btf_data.to_bytes();
1✔
343
  std::stringstream generated_output;
1✔
344
  btf_data.to_json(generated_output);
1✔
345

346
  std::stringstream generated_output_round_trip;
1✔
347
  btf_data_round_trip.to_json(generated_output_round_trip);
1✔
348

349
  std::string expected_json = "{\n"
350
                              "  \"btf_kinds\": [\n"
351
                              "    {\n"
352
                              "      \"id\": 1,\n"
353
                              "      \"kind_type\": \"BTF_KIND_ENUM64\",\n"
354
                              "      \"name\": \"enum_type\",\n"
355
                              "      \"size_in_bytes\": 0,\n"
356
                              "      \"is_signed\": false,\n"
357
                              "      \"members\": [\n"
358
                              "        {\n"
359
                              "          \"name\": \"A\",\n"
360
                              "          \"value\": 18446744073709551515\n"
361
                              "        },\n"
362
                              "        {\n"
363
                              "          \"name\": \"B\",\n"
364
                              "          \"value\": 18446744073709551516\n"
365
                              "        },\n"
366
                              "        {\n"
367
                              "          \"name\": \"C\",\n"
368
                              "          \"value\": 18446744073709551517\n"
369
                              "        }\n"
370
                              "      ]\n"
371
                              "    }\n"
372
                              "  ]\n"
373
                              "}\n";
1✔
374

375
  std::string pretty_printed_generated_output =
376
      libbtf::pretty_print_json(generated_output.str());
1✔
377

378
  // Compare the pretty printed JSON to the expected JSON.
379
  std::stringstream generated_json_stream(pretty_printed_generated_output);
1✔
380
  std::stringstream expected_json_stream(expected_json);
1✔
381
  verify_line_by_line(generated_json_stream, expected_json_stream);
1✔
382

383
  // Compare the generated JSON to the round trip JSON.
384
  REQUIRE(generated_output.str() == generated_output_round_trip.str());
5✔
385
}
2✔
386

387
TEST_CASE("modifiers", "[parsing][json]") {
1✔
388
  libbtf::btf_type_data btf_data;
1✔
389

390
  // The following BTF type doesn't make sense, but it's useful for testing.
391
  // Add:
392
  // Volatile
393
  // Const
394
  // Restrict
395
  // Float
396

397
  // Each type points to the next one.
398
  btf_data.append(libbtf::btf_kind_volatile{.type = 2});
2✔
399
  btf_data.append(libbtf::btf_kind_const{.type = 3});
2✔
400
  btf_data.append(libbtf::btf_kind_restrict{.type = 4});
1✔
401

402
  // Add a float type.
403
  btf_data.append(
2✔
404
      libbtf::btf_kind_float{.name = "float_type", .size_in_bytes = 8});
1✔
405

406
  libbtf::btf_type_data btf_data_round_trip = btf_data.to_bytes();
1✔
407
  std::stringstream generated_output;
1✔
408
  btf_data.to_json(generated_output);
1✔
409

410
  std::stringstream generated_output_round_trip;
1✔
411
  btf_data_round_trip.to_json(generated_output_round_trip);
1✔
412

413
  REQUIRE(generated_output.str() == generated_output_round_trip.str());
5✔
414

415
  std::string expected_json =
416
      "{\n"
417
      "  \"btf_kinds\": [\n"
418
      "    {\n"
419
      "      \"id\": 1,\n"
420
      "      \"kind_type\": \"BTF_KIND_VOLATILE\",\n"
421
      "      \"type\": {\n"
422
      "        \"id\": 2,\n"
423
      "        \"kind_type\": \"BTF_KIND_CONST\",\n"
424
      "        \"type\": {\n"
425
      "          \"id\": 3,\n"
426
      "          \"kind_type\": \"BTF_KIND_RESTRICT\",\n"
427
      "          \"type\": {\n"
428
      "            \"id\": 4,\n"
429
      "            \"kind_type\": \"BTF_KIND_FLOAT\",\n"
430
      "            \"name\": \"float_type\",\n"
431
      "            \"size_in_bytes\": 8\n"
432
      "          }\n"
433
      "        }\n"
434
      "      }\n"
435
      "    }\n"
436
      "  ]\n"
437
      "}\n";
1✔
438
  std::string pretty_printed_generated_output =
439
      libbtf::pretty_print_json(generated_output.str());
1✔
440

441
  // Compare the pretty printed JSON to the expected JSON.
442
  std::stringstream generated_json_stream(pretty_printed_generated_output);
1✔
443
  std::stringstream expected_json_stream(expected_json);
1✔
444
  verify_line_by_line(generated_json_stream, expected_json_stream);
1✔
445
}
2✔
446

447
TEST_CASE("type_tag", "[parsing][json]") {
1✔
448
  libbtf::btf_type_data btf_data;
1✔
449

450
  // The following BTF type doesn't make sense, but it's useful for testing.
451
  // Add:
452
  // ptr
453
  // type_tag
454
  // const
455
  // volatile
456
  // restrict
457
  // typedef
458
  // int
459

460
  // Each type points to the next one.
461
  btf_data.append(libbtf::btf_kind_ptr{.type = 2});
1✔
462
  btf_data.append(libbtf::btf_kind_type_tag{.name = "type_tag", .type = 3});
3✔
463
  btf_data.append(libbtf::btf_kind_const{.type = 4});
2✔
464
  btf_data.append(libbtf::btf_kind_volatile{.type = 5});
2✔
465
  btf_data.append(libbtf::btf_kind_restrict{.type = 6});
1✔
466
  btf_data.append(libbtf::btf_kind_typedef{.name = "typedef", .type = 7});
3✔
467
  btf_data.append(libbtf::btf_kind_int{.name = "int_type", .size_in_bytes = 8});
3✔
468

469
  libbtf::btf_type_data btf_data_round_trip = btf_data.to_bytes();
1✔
470

471
  std::stringstream generated_output;
1✔
472
  btf_data.to_json(generated_output);
1✔
473

474
  std::stringstream generated_output_round_trip;
1✔
475
  btf_data_round_trip.to_json(generated_output_round_trip);
1✔
476

477
  REQUIRE(generated_output.str() == generated_output_round_trip.str());
5✔
478

479
  std::string expected_json =
480
      "{\n"
481
      "  \"btf_kinds\": [\n"
482
      "    {\n"
483
      "      \"id\": 1,\n"
484
      "      \"kind_type\": \"BTF_KIND_PTR\",\n"
485
      "      \"type\": {\n"
486
      "        \"id\": 2,\n"
487
      "        \"kind_type\": \"BTF_KIND_TYPE_TAG\",\n"
488
      "        \"name\": \"type_tag\",\n"
489
      "        \"type\": {\n"
490
      "          \"id\": 3,\n"
491
      "          \"kind_type\": \"BTF_KIND_CONST\",\n"
492
      "          \"type\": {\n"
493
      "            \"id\": 4,\n"
494
      "            \"kind_type\": \"BTF_KIND_VOLATILE\",\n"
495
      "            \"type\": {\n"
496
      "              \"id\": 5,\n"
497
      "              \"kind_type\": \"BTF_KIND_RESTRICT\",\n"
498
      "              \"type\": {\n"
499
      "                \"id\": 6,\n"
500
      "                \"kind_type\": \"BTF_KIND_TYPEDEF\",\n"
501
      "                \"name\": \"typedef\",\n"
502
      "                \"type\": {\n"
503
      "                  \"id\": 7,\n"
504
      "                  \"kind_type\": \"BTF_KIND_INT\",\n"
505
      "                  \"name\": \"int_type\",\n"
506
      "                  \"size_in_bytes\": 8,\n"
507
      "                  \"offset_from_start_in_bits\": 0,\n"
508
      "                  \"field_width_in_bits\": 0,\n"
509
      "                  \"is_signed\": false,\n"
510
      "                  \"is_char\": false,\n"
511
      "                  \"is_bool\": false\n"
512
      "                }\n"
513
      "              }\n"
514
      "            }\n"
515
      "          }\n"
516
      "        }\n"
517
      "      }\n"
518
      "    }\n"
519
      "  ]\n"
520
      "}\n";
1✔
521
  std::string pretty_printed_generated_output =
522
      libbtf::pretty_print_json(generated_output.str());
1✔
523

524
  // Compare the pretty printed JSON to the expected JSON.
525
  std::stringstream generated_json_stream(pretty_printed_generated_output);
1✔
526
  std::stringstream expected_json_stream(expected_json);
1✔
527
  verify_line_by_line(generated_json_stream, expected_json_stream);
1✔
528
}
2✔
529

530
TEST_CASE("decl_tag", "[parsing][json]") {
1✔
531
  libbtf::btf_type_data btf_data;
1✔
532

533
  // The following BTF type doesn't make sense, but it's useful for testing.
534
  // Add:
535
  // delc_tag
536
  // int
537

538
  btf_data.append(libbtf::btf_kind_decl_tag{
3✔
539
      .name = "decl_tag", .type = 2, .component_index = 3});
540
  btf_data.append(libbtf::btf_kind_int{.name = "int_type", .size_in_bytes = 8});
3✔
541

542
  libbtf::btf_type_data btf_data_round_trip = btf_data.to_bytes();
1✔
543

544
  std::stringstream generated_output;
1✔
545
  btf_data.to_json(generated_output);
1✔
546

547
  std::stringstream generated_output_round_trip;
1✔
548
  btf_data_round_trip.to_json(generated_output_round_trip);
1✔
549

550
  REQUIRE(generated_output.str() == generated_output_round_trip.str());
5✔
551

552
  std::string expected_json = "{\n"
553
                              "  \"btf_kinds\": [\n"
554
                              "    {\n"
555
                              "      \"id\": 1,\n"
556
                              "      \"kind_type\": \"BTF_KIND_DECL_TAG\",\n"
557
                              "      \"name\": \"decl_tag\",\n"
558
                              "      \"type\": {\n"
559
                              "        \"id\": 2,\n"
560
                              "        \"kind_type\": \"BTF_KIND_INT\",\n"
561
                              "        \"name\": \"int_type\",\n"
562
                              "        \"size_in_bytes\": 8,\n"
563
                              "        \"offset_from_start_in_bits\": 0,\n"
564
                              "        \"field_width_in_bits\": 0,\n"
565
                              "        \"is_signed\": false,\n"
566
                              "        \"is_char\": false,\n"
567
                              "        \"is_bool\": false\n"
568
                              "      }\n"
569
                              "    }\n"
570
                              "  ]\n"
571
                              "}\n";
1✔
572
  std::string pretty_printed_generated_output =
573
      libbtf::pretty_print_json(generated_output.str());
1✔
574

575
  // Compare the pretty printed JSON to the expected JSON.
576
  std::stringstream generated_json_stream(pretty_printed_generated_output);
1✔
577
  std::stringstream expected_json_stream(expected_json);
1✔
578
  verify_line_by_line(generated_json_stream, expected_json_stream);
1✔
579
}
2✔
580

581
TEST_CASE("btf_maps_map_in_map", "[parsing][json]") {
1✔
582
  auto reader = ELFIO::elfio();
1✔
583
  std::string file = "map_in_map";
1✔
584
  REQUIRE(reader.load(std::string(TEST_OBJECT_FILE_DIRECTORY) + file + ".o"));
2✔
585

586
  auto btf = reader.sections[".BTF"];
1✔
587

588
  libbtf::btf_type_data btf_data = std::vector<std::byte>(
×
589
      {reinterpret_cast<const std::byte *>(btf->get_data()),
1✔
590
       reinterpret_cast<const std::byte *>(btf->get_data() + btf->get_size())});
1✔
591

592
  auto map_definitions = libbtf::parse_btf_map_section(btf_data);
1✔
593
  REQUIRE(map_definitions.size() == 2);
2✔
594

595
  // Verify that each map was parsed correctly.
596
  REQUIRE(map_definitions[1].name == "array_of_maps");
2✔
597
  REQUIRE(map_definitions[1].map_type == 12); // BPF_MAP_TYPE_ARRAY_OF_MAPS
2✔
598
  REQUIRE(map_definitions[1].key_size == 4);
2✔
599
  REQUIRE(map_definitions[1].value_size == 4);
2✔
600
  REQUIRE(map_definitions[1].max_entries == 1);
2✔
601
  REQUIRE(map_definitions[1].inner_map_type_id != 0);
2✔
602

603
  REQUIRE(map_definitions[0].name == "inner_map");
2✔
604
  REQUIRE(map_definitions[0].map_type == 2); // BPF_MAP_TYPE_ARRAY
2✔
605
  REQUIRE(map_definitions[0].key_size == 4);
2✔
606
  REQUIRE(map_definitions[0].value_size == 4);
2✔
607
  REQUIRE(map_definitions[0].max_entries == 1);
2✔
608
  REQUIRE(map_definitions[0].inner_map_type_id == 0);
2✔
609
}
2✔
610

611
TEST_CASE("btf_maps_ringbuf_in_map", "[parsing][json]") {
1✔
612
  auto reader = ELFIO::elfio();
1✔
613
  std::string file = "ringbuf_in_map";
1✔
614
  REQUIRE(reader.load(std::string(TEST_OBJECT_FILE_DIRECTORY) + file + ".o"));
2✔
615

616
  auto btf = reader.sections[".BTF"];
1✔
617

618
  libbtf::btf_type_data btf_data = std::vector<std::byte>(
×
619
      {reinterpret_cast<const std::byte *>(btf->get_data()),
1✔
620
       reinterpret_cast<const std::byte *>(btf->get_data() + btf->get_size())});
1✔
621

622
  auto map_definitions = libbtf::parse_btf_map_section(btf_data);
1✔
623
  REQUIRE(map_definitions.size() == 2);
2✔
624

625
  // Verify that each map was parsed correctly.
626
  REQUIRE(map_definitions[1].name == "array_of_maps");
2✔
627
  REQUIRE(map_definitions[1].map_type == 12); // BPF_MAP_TYPE_ARRAY_OF_MAPS
2✔
628
  REQUIRE(map_definitions[1].key_size == 4);
2✔
629
  REQUIRE(map_definitions[1].value_size == 4);
2✔
630
  REQUIRE(map_definitions[1].max_entries == 1);
2✔
631
  REQUIRE(map_definitions[1].inner_map_type_id != 0);
2✔
632

633
  REQUIRE(map_definitions[0].name == "inner_map");
2✔
634
  REQUIRE(map_definitions[0].map_type == 27); // BPF_MAP_TYPE_ARRAY
2✔
635
  REQUIRE(map_definitions[0].max_entries == 256 * 1024);
2✔
636
}
2✔
637

638
TEST_CASE("btf_maps_map_in_map_anonymous", "[parsing][json]") {
1✔
639
  auto reader = ELFIO::elfio();
1✔
640
  std::string file = "map_in_map_anonymous";
1✔
641
  REQUIRE(reader.load(std::string(TEST_OBJECT_FILE_DIRECTORY) + file + ".o"));
2✔
642

643
  auto btf = reader.sections[".BTF"];
1✔
644

645
  libbtf::btf_type_data btf_data = std::vector<std::byte>(
×
646
      {reinterpret_cast<const std::byte *>(btf->get_data()),
1✔
647
       reinterpret_cast<const std::byte *>(btf->get_data() + btf->get_size())});
1✔
648

649
  auto map_definitions = libbtf::parse_btf_map_section(btf_data);
1✔
650
  REQUIRE(map_definitions.size() == 2);
2✔
651

652
  // Verify that each map was parsed correctly.
653
  REQUIRE(map_definitions[1].name == "outer_map");
2✔
654
  REQUIRE(map_definitions[1].map_type == 12); // BPF_MAP_TYPE_ARRAY_OF_MAPS
2✔
655
  REQUIRE(map_definitions[1].key_size == 4);
2✔
656
  REQUIRE(map_definitions[1].value_size == 4);
2✔
657
  REQUIRE(map_definitions[1].max_entries == 1);
2✔
658
  REQUIRE(map_definitions[1].inner_map_type_id != 0);
2✔
659

660
  REQUIRE(map_definitions[0].name == "");
2✔
661
  REQUIRE(map_definitions[0].map_type == 2); // BPF_MAP_TYPE_ARRAY
2✔
662
  REQUIRE(map_definitions[0].key_size == 4);
2✔
663
  REQUIRE(map_definitions[0].value_size == 4);
2✔
664
  REQUIRE(map_definitions[0].max_entries == 1);
2✔
665
  REQUIRE(map_definitions[0].inner_map_type_id == 0);
2✔
666
}
2✔
667

668
TEST_CASE("btf_maps_map_in_map_typedef", "[parsing][json]") {
1✔
669
  auto reader = ELFIO::elfio();
1✔
670
  std::string file = "map_in_map_typedef";
1✔
671
  REQUIRE(reader.load(std::string(TEST_OBJECT_FILE_DIRECTORY) + file + ".o"));
2✔
672

673
  auto btf = reader.sections[".BTF"];
1✔
674

675
  libbtf::btf_type_data btf_data = std::vector<std::byte>(
×
676
      {reinterpret_cast<const std::byte *>(btf->get_data()),
1✔
677
       reinterpret_cast<const std::byte *>(btf->get_data() + btf->get_size())});
1✔
678

679
  auto map_definitions = libbtf::parse_btf_map_section(btf_data);
1✔
680
  REQUIRE(map_definitions.size() == 3);
2✔
681

682
  // Verify that each map was parsed correctly.
683
  REQUIRE(map_definitions[1].name == "outer_map_1");
2✔
684
  REQUIRE(map_definitions[1].map_type == 12); // BPF_MAP_TYPE_ARRAY_OF_MAPS
2✔
685
  REQUIRE(map_definitions[1].key_size == 4);
2✔
686
  REQUIRE(map_definitions[1].value_size == 4);
2✔
687
  REQUIRE(map_definitions[1].max_entries == 1);
2✔
688
  REQUIRE(map_definitions[1].inner_map_type_id != 0);
2✔
689

690
  REQUIRE(map_definitions[2].name == "outer_map_2");
2✔
691
  REQUIRE(map_definitions[2].map_type == 12); // BPF_MAP_TYPE_ARRAY_OF_MAPS
2✔
692
  REQUIRE(map_definitions[2].key_size == 4);
2✔
693
  REQUIRE(map_definitions[2].value_size == 4);
2✔
694
  REQUIRE(map_definitions[2].max_entries == 1);
2✔
695
  REQUIRE(map_definitions[2].inner_map_type_id != 0);
2✔
696

697
  REQUIRE(map_definitions[0].name == "");
2✔
698
  REQUIRE(map_definitions[0].map_type == 2); // BPF_MAP_TYPE_ARRAY
2✔
699
  REQUIRE(map_definitions[0].key_size == 4);
2✔
700
  REQUIRE(map_definitions[0].value_size == 4);
2✔
701
  REQUIRE(map_definitions[0].max_entries == 1);
2✔
702
  REQUIRE(map_definitions[0].inner_map_type_id == 0);
2✔
703
}
2✔
704

705
TEST_CASE("btf_maps_prog_array", "[parsing][json]") {
1✔
706
  auto reader = ELFIO::elfio();
1✔
707
  std::string file = "prog_array";
1✔
708
  REQUIRE(reader.load(std::string(TEST_OBJECT_FILE_DIRECTORY) + file + ".o"));
2✔
709

710
  auto btf = reader.sections[".BTF"];
1✔
711

712
  libbtf::btf_type_data btf_data = std::vector<std::byte>(
×
713
      {reinterpret_cast<const std::byte *>(btf->get_data()),
1✔
714
       reinterpret_cast<const std::byte *>(btf->get_data() + btf->get_size())});
1✔
715

716
  auto map_definitions = libbtf::parse_btf_map_section(btf_data);
1✔
717
  REQUIRE(map_definitions.size() == 1);
2✔
718

719
  // Verify that each map was parsed correctly.
720
  REQUIRE(map_definitions[0].name == "prog_array_map");
2✔
721
  REQUIRE(map_definitions[0].map_type == 3); // BPF_MAP_TYPE_PROG_ARRAY
2✔
722
  REQUIRE(map_definitions[0].key_size == 4);
2✔
723
  REQUIRE(map_definitions[0].value_size == 4);
2✔
724
  REQUIRE(map_definitions[0].max_entries == 4);
2✔
725
  REQUIRE(map_definitions[0].inner_map_type_id == 0);
2✔
726
}
2✔
727

728
TEST_CASE("get_unknown_type_id", "[btf_type_data][negative]") {
1✔
729
  libbtf::btf_type_data btf_data;
1✔
730
  REQUIRE_THROWS(btf_data.get_kind_index(1));
1✔
731
}
1✔
732

733
TEST_CASE("get_type_by_name_unknown", "[btf_type_data][negative]") {
1✔
734
  libbtf::btf_type_data btf_data;
1✔
735
  REQUIRE(btf_data.get_id("unknown") == 0);
2✔
736
}
1✔
737

738
TEST_CASE("dereference_non_pointer", "[btf_type_data][negative]") {
1✔
739
  libbtf::btf_type_data btf_data;
1✔
740
  btf_data.append(libbtf::btf_kind_int{.name = "int_type", .size_in_bytes = 8});
3✔
741
  REQUIRE_THROWS(btf_data.dereference_pointer(1));
1✔
742
}
1✔
743

744
TEST_CASE("build_btf_map_section", "[btf_type_data]") {
1✔
745
  std::vector<libbtf::btf_map_definition> map_definitions;
746
  map_definitions.push_back(
747
      libbtf::btf_map_definition{.name = "array_of_maps",
1✔
748
                                 .type_id = 1,
749
                                 .map_type = 12, // BPF_MAP_TYPE_ARRAY_OF_MAPS
750
                                 .key_size = 4,
751
                                 .value_size = 4,
752
                                 .max_entries = 1,
753
                                 .inner_map_type_id = 2});
754

755
  map_definitions.push_back(
756
      libbtf::btf_map_definition{.name = "inner_map",
1✔
757
                                 .type_id = 2,
758
                                 .map_type = 2, // BPF_MAP_TYPE_ARRAY
759
                                 .key_size = 4,
760
                                 .value_size = 4,
761
                                 .max_entries = 1,
762
                                 .inner_map_type_id = 0});
763
  libbtf::btf_type_data btf_data;
1✔
764
  libbtf::build_btf_map_section(map_definitions, btf_data);
1✔
765

766
  std::vector<libbtf::btf_map_definition> generated_map_definitions =
767
      libbtf::parse_btf_map_section(btf_data);
1✔
768
  REQUIRE(generated_map_definitions.size() == map_definitions.size());
2✔
769
  for (size_t i = 0; i < generated_map_definitions.size(); i++) {
3✔
770
    REQUIRE(generated_map_definitions[i].name == map_definitions[i].name);
4✔
771
    REQUIRE(generated_map_definitions[i].map_type ==
4✔
772
            map_definitions[i].map_type);
773
    REQUIRE(generated_map_definitions[i].key_size ==
4✔
774
            map_definitions[i].key_size);
775
    REQUIRE(generated_map_definitions[i].value_size ==
4✔
776
            map_definitions[i].value_size);
777
    REQUIRE(generated_map_definitions[i].max_entries ==
4✔
778
            map_definitions[i].max_entries);
779
  }
780
  REQUIRE(generated_map_definitions[0].inner_map_type_id ==
2✔
781
          generated_map_definitions[1].type_id);
782
}
1✔
783

784
TEST_CASE("parse_btf_map_section_globals", "[btf_type_data]") {
1✔
785
  auto reader = ELFIO::elfio();
1✔
786
  std::string file = "global_variable";
1✔
787
  REQUIRE(reader.load(std::string(TEST_OBJECT_FILE_DIRECTORY) + file + ".o"));
2✔
788

789
  auto btf = reader.sections[".BTF"];
1✔
790

791
  libbtf::btf_type_data btf_data = std::vector<std::byte>(
×
792
      {reinterpret_cast<const std::byte *>(btf->get_data()),
1✔
793
       reinterpret_cast<const std::byte *>(btf->get_data() + btf->get_size())});
1✔
794

795
  auto map_definitions = libbtf::parse_btf_map_section(btf_data);
1✔
796

797
  REQUIRE(map_definitions.size() == 3);
2✔
798
  REQUIRE(map_definitions[0].name == ".bss");
2✔
799
  REQUIRE(map_definitions[0].type_id == 15);
2✔
800
  REQUIRE(map_definitions[0].map_type == 0); // Undefined type
2✔
801
  REQUIRE(map_definitions[0].key_size == 4);
2✔
802
  REQUIRE(map_definitions[0].value_size == 8);
2✔
803
  REQUIRE(map_definitions[0].max_entries == 1);
2✔
804
  REQUIRE(map_definitions[0].inner_map_type_id == 0);
2✔
805

806
  REQUIRE(map_definitions[1].name == ".data");
2✔
807
  REQUIRE(map_definitions[1].type_id == 16);
2✔
808
  REQUIRE(map_definitions[1].map_type == 0); // Undefined type
2✔
809
  REQUIRE(map_definitions[1].key_size == 4);
2✔
810
  REQUIRE(map_definitions[1].value_size == 40);
2✔
811
  REQUIRE(map_definitions[1].max_entries == 1);
2✔
812
  REQUIRE(map_definitions[1].inner_map_type_id == 0);
2✔
813

814
  REQUIRE(map_definitions[2].name == ".rodata");
2✔
815
  REQUIRE(map_definitions[2].type_id == 17);
2✔
816
  REQUIRE(map_definitions[2].map_type == 0); // Undefined type
2✔
817
  REQUIRE(map_definitions[2].key_size == 4);
2✔
818
  REQUIRE(map_definitions[2].value_size == 4);
2✔
819
  REQUIRE(map_definitions[2].max_entries == 1);
2✔
820
  REQUIRE(map_definitions[2].inner_map_type_id == 0);
2✔
821

822
}
2✔
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