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

vbpf / ebpf-verifier / 14231336081

02 Apr 2025 11:08PM UTC coverage: 87.272% (-0.9%) from 88.177%
14231336081

push

github

web-flow
Propogate ebpf_verifier_options_t to thread_local_options (#856)

Signed-off-by: Alan Jowett <alanjo@microsoft.com>

5 of 5 new or added lines in 2 files covered. (100.0%)

58 existing lines in 19 files now uncovered.

8324 of 9538 relevant lines covered (87.27%)

4881701.3 hits per line

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

52.93
/src/asm_ostream.cpp
1
// Copyright (c) Prevail Verifier contributors.
2
// SPDX-License-Identifier: MIT
3
#include <fstream>
4
#include <iomanip>
5
#include <iostream>
6
#include <variant>
7
#include <vector>
8

9
#include "asm_syntax.hpp"
10
#include "crab/cfg.hpp"
11
#include "crab/fwd_analyzer.hpp"
12
#include "crab/interval.hpp"
13
#include "crab/type_encoding.hpp"
14
#include "crab/variable.hpp"
15
#include "crab_utils/num_big.hpp"
16
#include "crab_verifier.hpp"
17
#include "helpers.hpp"
18
#include "platform.hpp"
19
#include "spec_type_descriptors.hpp"
20

21
using crab::TypeGroup;
22
using std::optional;
23
using std::string;
24
using std::vector;
25

26
namespace crab {
27

28
std::ostream& operator<<(std::ostream& o, const interval_t& interval) {
410✔
29
    if (interval.is_bottom()) {
410✔
30
        o << "_|_";
×
31
    } else {
32
        o << "[" << interval._lb << ", " << interval._ub << "]";
410✔
33
    }
34
    return o;
410✔
35
}
36
std::ostream& operator<<(std::ostream& o, const number_t& z) { return o << z._n.str(); }
175,144✔
37

38
std::string number_t::to_string() const { return _n.str(); }
×
39

40
std::string interval_t::to_string() const {
×
41
    std::ostringstream s;
×
42
    s << *this;
×
43
    return s.str();
×
44
}
×
45

46
std::ostream& operator<<(std::ostream& os, const label_t& label) {
796✔
47
    if (label == label_t::entry) {
796✔
48
        return os << "entry";
3✔
49
    }
50
    if (label == label_t::exit) {
793✔
51
        return os << "exit";
3✔
52
    }
53
    if (!label.stack_frame_prefix.empty()) {
790✔
54
        os << label.stack_frame_prefix << STACK_FRAME_DELIMITER;
165✔
55
    }
56
    os << label.from;
790✔
57
    if (label.to != -1) {
790✔
58
        os << ":" << label.to;
25✔
59
    }
60
    if (!label.special_label.empty()) {
790✔
61
        os << " (" << label.special_label << ")";
10✔
62
    }
63
    return os;
64
}
65

66
string to_string(label_t const& label) {
773✔
67
    std::stringstream str;
773✔
68
    str << label;
773✔
69
    return str.str();
1,546✔
70
}
773✔
71

72
} // namespace crab
73

UNCOV
74
struct LineInfoPrinter {
×
75
    std::ostream& os;
76
    std::string previous_source_line;
77

78
    void print_line_info(const label_t& label) {
×
79
        if (thread_local_options.verbosity_opts.print_line_info) {
×
80
            const auto& line_info_map = thread_local_program_info.get().line_info;
×
81
            const auto& line_info = line_info_map.find(label.from);
×
82
            // Print line info only once.
83
            if (line_info != line_info_map.end() && line_info->second.source_line != previous_source_line) {
×
84
                os << "\n" << line_info->second << "\n";
×
85
                previous_source_line = line_info->second.source_line;
×
86
            }
87
        }
88
    }
×
89
};
90

91
void print_jump(std::ostream& o, const std::string& direction, const std::set<label_t>& labels) {
×
92
    auto [it, et] = std::pair{labels.begin(), labels.end()};
×
93
    if (it != et) {
×
94
        o << "  " << direction << " ";
×
95
        while (it != et) {
×
96
            o << *it;
×
97
            ++it;
×
98
            if (it == et) {
×
99
                o << ";";
×
100
            } else {
101
                o << ",";
×
102
            }
103
        }
104
    }
105
    o << "\n";
×
106
}
×
107

108
void print_program(const Program& prog, std::ostream& os, const bool simplify, const printfunc& prefunc,
×
109
                   const printfunc& postfunc) {
110
    LineInfoPrinter printer{os};
×
111
    for (const crab::basic_block_t& bb : crab::basic_block_t::collect_basic_blocks(prog.cfg(), simplify)) {
×
112
        prefunc(os, bb.first_label());
×
113
        print_jump(os, "from", prog.cfg().parents_of(bb.first_label()));
×
114
        os << bb.first_label() << ":\n";
×
115
        for (const label_t& label : bb) {
×
116
            printer.print_line_info(label);
×
117
            for (const auto& pre : prog.assertions_at(label)) {
×
118
                os << "  " << "assert " << pre << ";\n";
×
119
            }
×
120
            os << "  " << prog.instruction_at(label) << ";\n";
×
121
        }
122
        print_jump(os, "goto", prog.cfg().children_of(bb.last_label()));
×
123
        postfunc(os, bb.last_label());
×
124
    }
×
125
    os << "\n";
×
126
}
×
127

128
static void nop(std::ostream&, const label_t&) {}
×
129

130
void print_program(const Program& prog, std::ostream& os, const bool simplify) {
×
131
    print_program(prog, os, simplify, nop, nop);
×
132
}
×
133

134
void print_dot(const Program& prog, std::ostream& out) {
×
135
    out << "digraph program {\n";
×
136
    out << "    node [shape = rectangle];\n";
×
137
    for (const auto& label : prog.labels()) {
×
138
        out << "    \"" << label << "\"[xlabel=\"" << label << "\",label=\"";
×
139

140
        for (const auto& pre : prog.assertions_at(label)) {
×
141
            out << "assert " << pre << "\\l";
×
142
        }
×
143
        out << prog.instruction_at(label) << "\\l";
×
144

145
        out << "\"];\n";
×
146
        for (const label_t& next : prog.cfg().children_of(label)) {
×
147
            out << "    \"" << label << "\" -> \"" << next << "\";\n";
×
148
        }
149
        out << "\n";
×
150
    }
151
    out << "}\n";
×
152
}
×
153

154
void print_dot(const Program& prog, const std::string& outfile) {
×
155
    std::ofstream out{outfile};
×
156
    if (out.fail()) {
×
157
        throw std::runtime_error(std::string("Could not open file ") + outfile);
×
158
    }
159
    print_dot(prog, out);
×
160
}
×
161

162
void print_reachability(std::ostream& os, const Report& report) {
×
163
    for (const auto& [label, notes] : report.reachability) {
×
164
        for (const auto& msg : notes) {
×
165
            os << label << ": " << msg << "\n";
×
166
        }
167
    }
168
    os << "\n";
×
169
}
×
170

171
void print_warnings(std::ostream& os, const Report& report) {
×
172
    LineInfoPrinter printer{os};
×
173
    for (const auto& [label, warnings] : report.warnings) {
×
174
        for (const auto& msg : warnings) {
×
175
            printer.print_line_info(label);
×
176
            os << label << ": " << msg << "\n";
×
177
        }
178
    }
179
    os << "\n";
×
180
}
×
181

182
void print_all_messages(std::ostream& os, const Report& report) {
×
183
    print_reachability(os, report);
×
184
    print_warnings(os, report);
×
185
}
×
186

187
void print_invariants(std::ostream& os, const Program& prog, const bool simplify, const Invariants& invariants) {
×
188
    print_program(
×
189
        prog, os, simplify,
190
        [&](std::ostream& os, const label_t& label) -> void {
×
191
            os << "\nPre-invariant : " << invariants.invariants.at(label).pre << "\n";
×
192
        },
×
193
        [&](std::ostream& os, const label_t& label) -> void {
×
194
            os << "\nPost-invariant : " << invariants.invariants.at(label).post << "\n";
×
195
        });
×
196
}
×
197

198
namespace asm_syntax {
199
std::ostream& operator<<(std::ostream& os, const ArgSingle::Kind kind) {
37✔
200
    switch (kind) {
37✔
201
    case ArgSingle::Kind::ANYTHING: return os << "uint64_t";
7✔
202
    case ArgSingle::Kind::PTR_TO_CTX: return os << "ctx";
2✔
203
    case ArgSingle::Kind::MAP_FD: return os << "map_fd";
12✔
204
    case ArgSingle::Kind::MAP_FD_PROGRAMS: return os << "map_fd_programs";
×
205
    case ArgSingle::Kind::PTR_TO_MAP_KEY: return os << "map_key";
12✔
206
    case ArgSingle::Kind::PTR_TO_MAP_VALUE: return os << "map_value";
4✔
207
    }
208
    assert(false);
209
    return os;
210
}
211

212
std::ostream& operator<<(std::ostream& os, const ArgPair::Kind kind) {
×
213
    switch (kind) {
×
214
    case ArgPair::Kind::PTR_TO_READABLE_MEM: return os << "mem";
×
215
    case ArgPair::Kind::PTR_TO_READABLE_MEM_OR_NULL: return os << "mem?";
×
216
    case ArgPair::Kind::PTR_TO_WRITABLE_MEM: return os << "out";
×
217
    }
218
    assert(false);
219
    return os;
220
}
221

222
std::ostream& operator<<(std::ostream& os, const ArgSingle arg) {
37✔
223
    os << arg.kind << " " << arg.reg;
37✔
224
    return os;
37✔
225
}
226

227
std::ostream& operator<<(std::ostream& os, const ArgPair arg) {
×
228
    os << arg.kind << " " << arg.mem << "[" << arg.size;
×
229
    if (arg.can_be_zero) {
×
230
        os << "?";
×
231
    }
232
    os << "], uint64_t " << arg.size;
×
233
    return os;
×
234
}
235

236
std::ostream& operator<<(std::ostream& os, const Bin::Op op) {
139✔
237
    using Op = Bin::Op;
139✔
238
    switch (op) {
139✔
239
    case Op::MOV: return os;
240
    case Op::MOVSX8: return os << "s8";
×
241
    case Op::MOVSX16: return os << "s16";
×
242
    case Op::MOVSX32: return os << "s32";
×
243
    case Op::ADD: return os << "+";
35✔
244
    case Op::SUB: return os << "-";
×
245
    case Op::MUL: return os << "*";
×
246
    case Op::UDIV: return os << "/";
×
247
    case Op::SDIV: return os << "s/";
×
248
    case Op::UMOD: return os << "%";
×
249
    case Op::SMOD: return os << "s%";
×
250
    case Op::OR: return os << "|";
×
251
    case Op::AND: return os << "&";
15✔
252
    case Op::LSH: return os << "<<";
6✔
253
    case Op::RSH: return os << ">>";
×
254
    case Op::ARSH: return os << ">>>";
1✔
255
    case Op::XOR: return os << "^";
1✔
256
    }
257
    assert(false);
258
    return os;
259
}
260

261
std::ostream& operator<<(std::ostream& os, const Condition::Op op) {
116✔
262
    using Op = Condition::Op;
116✔
263
    switch (op) {
116✔
264
    case Op::EQ: return os << "==";
19✔
265
    case Op::NE: return os << "!=";
6✔
266
    case Op::SET: return os << "&==";
×
267
    case Op::NSET: return os << "&!="; // not in ebpf
×
268
    case Op::LT: return os << "<";     // TODO: os << "u<";
66✔
269
    case Op::LE: return os << "<=";    // TODO: os << "u<=";
6✔
270
    case Op::GT: return os << ">";     // TODO: os << "u>";
11✔
271
    case Op::GE: return os << ">=";    // TODO: os << "u>=";
3✔
272
    case Op::SLT: return os << "s<";
2✔
273
    case Op::SLE: return os << "s<=";
×
274
    case Op::SGT: return os << "s>";
2✔
275
    case Op::SGE: return os << "s>=";
1✔
276
    }
277
    assert(false);
278
    return os;
279
}
280

281
static string size(const int w) { return string("u") + std::to_string(w * 8); }
184✔
282

283
// ReSharper disable CppMemberFunctionMayBeConst
284
struct AssertionPrinterVisitor {
285
    std::ostream& _os;
286

287
    void operator()(ValidStore const& a) {
1✔
288
        _os << a.mem << ".type != stack -> " << TypeConstraint{a.val, TypeGroup::number};
1✔
289
    }
1✔
290

291
    void operator()(ValidAccess const& a) {
24,393✔
292
        if (a.or_null) {
24,393✔
293
            _os << "(" << TypeConstraint{a.reg, TypeGroup::number} << " and " << a.reg << ".value == 0) or ";
2,520✔
294
        }
295
        _os << "valid_access(" << a.reg << ".offset";
24,393✔
296
        if (a.offset > 0) {
24,393✔
297
            _os << "+" << a.offset;
15,478✔
298
        } else if (a.offset < 0) {
8,915✔
299
            _os << a.offset;
50✔
300
        }
301

302
        if (a.width == Value{Imm{0}}) {
24,393✔
303
            // a.width == 0, meaning we only care it's an in-bound pointer,
304
            // so it can be compared with another pointer to the same region.
305
            _os << ") for comparison/subtraction";
2,092✔
306
        } else {
307
            _os << ", width=" << a.width << ") for ";
22,301✔
308
            if (a.access_type == AccessType::read) {
22,301✔
309
                _os << "read";
16,524✔
310
            } else {
311
                _os << "write";
5,777✔
312
            }
313
        }
314
    }
24,393✔
315

316
    void operator()(const BoundedLoopCount& a) {
16✔
317
        _os << crab::variable_t::loop_counter(to_string(a.name)) << " < " << a.limit;
16✔
318
    }
16✔
319

320
    static crab::variable_t typereg(const Reg& r) { return crab::variable_t::reg(crab::data_kind_t::types, r.v); }
3,277✔
321

322
    void operator()(ValidSize const& a) {
2,103✔
323
        const auto op = a.can_be_zero ? " >= " : " > ";
2,103✔
324
        _os << a.reg << ".value" << op << 0;
2,103✔
325
    }
2,103✔
326

327
    void operator()(ValidCall const& a) {
1✔
328
        const EbpfHelperPrototype proto = thread_local_program_info->platform->get_helper_prototype(a.func);
1✔
329
        _os << "valid call(" << proto.name << ")";
1✔
330
    }
1✔
331

332
    void operator()(ValidMapKeyValue const& a) {
71✔
333
        _os << "within stack(" << a.access_reg << ":" << (a.key ? "key_size" : "value_size") << "(" << a.map_fd_reg
107✔
334
            << "))";
71✔
335
    }
71✔
336

337
    void operator()(ZeroCtxOffset const& a) {
2,618✔
338
        _os << crab::variable_t::reg(crab::data_kind_t::ctx_offsets, a.reg.v) << " == 0";
2,618✔
339
    }
2,618✔
340

341
    void operator()(Comparable const& a) {
1,098✔
342
        if (a.or_r2_is_number) {
1,098✔
343
            _os << TypeConstraint{a.r2, TypeGroup::number} << " or ";
236✔
344
        }
345
        _os << typereg(a.r1) << " == " << typereg(a.r2) << " in " << TypeGroup::singleton_ptr;
1,098✔
346
    }
1,098✔
347

348
    void operator()(Addable const& a) {
2✔
349
        _os << TypeConstraint{a.ptr, TypeGroup::pointer} << " -> " << TypeConstraint{a.num, TypeGroup::number};
6✔
350
    }
2✔
351

352
    void operator()(ValidDivisor const& a) { _os << a.reg << " != 0"; }
38✔
353

354
    void operator()(TypeConstraint const& tc) {
1,078✔
355
        const string cmp_op = is_singleton_type(tc.types) ? "==" : "in";
1,104✔
356
        _os << typereg(tc.reg) << " " << cmp_op << " " << tc.types;
1,078✔
357
    }
1,078✔
358

359
    void operator()(FuncConstraint const& fc) { _os << typereg(fc.reg) << " is helper"; }
3✔
360
};
361

362
// ReSharper disable CppMemberFunctionMayBeConst
363
struct CommandPrinterVisitor {
364
    std::ostream& os_;
365

366
    void visit(const auto& item) { std::visit(*this, item); }
367

368
    void operator()(Undefined const& a) { os_ << "Undefined{" << a.opcode << "}"; }
×
369

370
    void operator()(LoadMapFd const& b) { os_ << b.dst << " = map_fd " << b.mapfd; }
12✔
371

372
    void operator()(LoadMapAddress const& b) { os_ << b.dst << " = map_val(" << b.mapfd << ") + " << b.offset; }
×
373

374
    // llvm-objdump uses "w<number>" for 32-bit operations and "r<number>" for 64-bit operations.
375
    // We use the same convention here for consistency.
376
    static std::string reg_name(Reg const& a, const bool is64) { return ((is64) ? "r" : "w") + std::to_string(a.v); }
278✔
377

378
    void operator()(Bin const& b) {
139✔
379
        os_ << reg_name(b.dst, b.is64) << " " << b.op << "= " << b.v;
278✔
380
        if (b.lddw) {
139✔
381
            os_ << " ll";
1✔
382
        }
383
    }
139✔
384

385
    void operator()(Un const& b) {
6✔
386
        os_ << b.dst << " = ";
6✔
387
        switch (b.op) {
6✔
388
        case Un::Op::BE16: os_ << "be16 "; break;
1✔
389
        case Un::Op::BE32: os_ << "be32 "; break;
1✔
390
        case Un::Op::BE64: os_ << "be64 "; break;
1✔
391
        case Un::Op::LE16: os_ << "le16 "; break;
1✔
392
        case Un::Op::LE32: os_ << "le32 "; break;
1✔
393
        case Un::Op::LE64: os_ << "le64 "; break;
1✔
394
        case Un::Op::SWAP16: os_ << "swap16 "; break;
×
395
        case Un::Op::SWAP32: os_ << "swap32 "; break;
×
396
        case Un::Op::SWAP64: os_ << "swap64 "; break;
×
397
        case Un::Op::NEG: os_ << "-"; break;
×
398
        }
399
        os_ << b.dst;
6✔
400
    }
6✔
401

402
    void operator()(Call const& call) {
35✔
403
        os_ << "r0 = " << call.name << ":" << call.func << "(";
35✔
404
        for (uint8_t r = 1; r <= 5; r++) {
72✔
405
            // Look for a singleton.
406
            auto single = std::ranges::find_if(call.singles, [r](const ArgSingle arg) { return arg.reg.v == r; });
186✔
407
            if (single != call.singles.end()) {
72✔
408
                if (r > 1) {
37✔
409
                    os_ << ", ";
24✔
410
                }
411
                os_ << *single;
37✔
412
                continue;
37✔
413
            }
414

415
            // Look for the start of a pair.
416
            auto pair = std::ranges::find_if(call.pairs, [r](const ArgPair arg) { return arg.mem.v == r; });
35✔
417
            if (pair != call.pairs.end()) {
35✔
418
                if (r > 1) {
×
419
                    os_ << ", ";
×
420
                }
421
                os_ << *pair;
×
422
                r++;
×
423
                continue;
×
424
            }
425

426
            // Not found.
427
            break;
35✔
428
        }
429
        os_ << ")";
35✔
430
    }
35✔
431

432
    void operator()(CallLocal const& call) { os_ << "call <" << to_string(call.target) << ">"; }
×
433

434
    void operator()(Callx const& callx) { os_ << "callx " << callx.func; }
×
435

436
    void operator()(Exit const& b) { os_ << "exit"; }
25✔
437

438
    void operator()(Jmp const& b) {
×
439
        // A "standalone" jump Instruction.
440
        // Print the label without offset calculations.
441
        if (b.cond) {
×
442
            os_ << "if ";
×
443
            print(*b.cond);
×
444
            os_ << " ";
×
445
        }
446
        os_ << "goto label <" << to_string(b.target) << ">";
×
447
    }
×
448

449
    void operator()(Jmp const& b, const int offset) {
27✔
450
        const string sign = offset > 0 ? "+" : "";
27✔
451
        const string target = sign + std::to_string(offset) + " <" + to_string(b.target) + ">";
81✔
452

453
        if (b.cond) {
27✔
454
            os_ << "if ";
20✔
455
            print(*b.cond);
20✔
456
            os_ << " ";
20✔
457
        }
458
        os_ << "goto " << target;
27✔
459
    }
27✔
460

461
    void operator()(Packet const& b) {
×
462
        /* Direct packet access, R0 = *(uint *) (skb->data + imm32) */
463
        /* Indirect packet access, R0 = *(uint *) (skb->data + src_reg + imm32) */
464
        const string s = size(b.width);
×
465
        os_ << "r0 = ";
×
466
        os_ << "*(" << s << " *)skb[";
×
467
        if (b.regoffset) {
×
468
            os_ << *b.regoffset;
×
469
        }
470
        if (b.offset != 0) {
×
471
            if (b.regoffset) {
×
472
                os_ << " + ";
×
473
            }
474
            os_ << b.offset;
×
475
        }
476
        os_ << "]";
×
477
    }
×
478

479
    void print(Deref const& access) {
92✔
480
        const string sign = access.offset < 0 ? " - " : " + ";
120✔
481
        int offset = std::abs(access.offset); // what about INT_MIN?
92✔
482
        os_ << "*(" << size(access.width) << " *)";
184✔
483
        os_ << "(" << access.basereg << sign << offset << ")";
92✔
484
    }
92✔
485

486
    void print(Condition const& cond) {
116✔
487
        os_ << cond.left << " " << ((!cond.is64) ? "w" : "") << cond.op << " " << cond.right;
157✔
488
    }
116✔
489

490
    void operator()(Mem const& b) {
92✔
491
        if (b.is_load) {
92✔
492
            os_ << b.value << " = ";
22✔
493
        }
494
        print(b.access);
92✔
495
        if (!b.is_load) {
92✔
496
            os_ << " = " << b.value;
70✔
497
        }
498
    }
92✔
499

500
    void operator()(Atomic const& b) {
×
501
        os_ << "lock ";
×
502
        print(b.access);
×
503
        os_ << " ";
×
504
        bool showfetch = true;
×
505
        switch (b.op) {
×
506
        case Atomic::Op::ADD: os_ << "+"; break;
×
507
        case Atomic::Op::OR: os_ << "|"; break;
×
508
        case Atomic::Op::AND: os_ << "&"; break;
×
509
        case Atomic::Op::XOR: os_ << "^"; break;
×
510
        case Atomic::Op::XCHG:
×
511
            os_ << "x";
×
512
            showfetch = false;
×
513
            break;
×
514
        case Atomic::Op::CMPXCHG:
×
515
            os_ << "cx";
×
516
            showfetch = false;
×
517
            break;
×
518
        }
519
        os_ << "= " << b.valreg;
×
520

521
        if (showfetch && b.fetch) {
×
522
            os_ << " fetch";
×
523
        }
524
    }
×
525

526
    void operator()(Assume const& b) {
96✔
527
        os_ << "assume ";
96✔
528
        print(b.cond);
96✔
529
    }
96✔
530

531
    void operator()(IncrementLoopCounter const& a) { os_ << crab::variable_t::loop_counter(to_string(a.name)) << "++"; }
×
532
};
533
// ReSharper restore CppMemberFunctionMayBeConst
534

535
std::ostream& operator<<(std::ostream& os, Instruction const& ins) {
97✔
UNCOV
536
    std::visit(CommandPrinterVisitor{os}, ins);
×
UNCOV
537
    return os;
×
538
}
539

540
string to_string(Instruction const& ins) {
97✔
541
    std::stringstream str;
97✔
542
    str << ins;
97✔
543
    return str.str();
194✔
544
}
97✔
545

546
std::ostream& operator<<(std::ostream& os, const Assertion& a) {
31,422✔
547
    std::visit(AssertionPrinterVisitor{os}, a);
963✔
548
    return os;
963✔
549
}
550

551
string to_string(Assertion const& constraint) {
30,459✔
552
    std::stringstream str;
30,459✔
553
    str << constraint;
30,459✔
554
    return str.str();
60,918✔
555
}
30,459✔
556

557
auto get_labels(const InstructionSeq& insts) {
19✔
558
    pc_t pc = 0;
19✔
559
    std::map<label_t, pc_t> pc_of_label;
19✔
560
    for (const auto& [label, inst, _] : insts) {
354✔
561
        pc_of_label[label] = pc;
335✔
562
        pc += size(inst);
335✔
563
    }
564
    return pc_of_label;
19✔
565
}
×
566

567
void print(const InstructionSeq& insts, std::ostream& out, const std::optional<const label_t>& label_to_print,
19✔
568
           const bool print_line_info) {
569
    const auto pc_of_label = get_labels(insts);
19✔
570
    pc_t pc = 0;
19✔
571
    std::string previous_source;
19✔
572
    CommandPrinterVisitor visitor{out};
19✔
573
    for (const LabeledInstruction& labeled_inst : insts) {
354✔
574
        const auto& [label, ins, line_info] = labeled_inst;
335✔
575
        if (!label_to_print.has_value() || label == label_to_print) {
335✔
576
            if (line_info.has_value() && print_line_info) {
335✔
577
                auto& [file, source, line, column] = line_info.value();
×
578
                // Only decorate the first instruction associated with a source line.
579
                if (source != previous_source) {
×
580
                    out << line_info.value();
×
581
                    previous_source = source;
×
582
                }
583
            }
584
            if (label.isjump()) {
335✔
585
                out << "\n";
×
586
                out << label << ":\n";
×
587
            }
588
            if (label_to_print.has_value()) {
335✔
589
                out << pc << ": ";
×
590
            } else {
591
                out << std::setw(8) << pc << ":\t";
335✔
592
            }
593
            if (const auto jmp = std::get_if<Jmp>(&ins)) {
335✔
594
                if (!pc_of_label.contains(jmp->target)) {
27✔
595
                    throw std::runtime_error(string("Cannot find label ") + crab::to_string(jmp->target));
×
596
                }
597
                const pc_t target_pc = pc_of_label.at(jmp->target);
27✔
598
                visitor(*jmp, target_pc - static_cast<int>(pc) - 1);
27✔
599
            } else {
600
                std::visit(visitor, ins);
308✔
601
            }
602
            out << "\n";
335✔
603
        }
604
        pc += size(ins);
335✔
605
    }
606
}
19✔
607

608
} // namespace asm_syntax
609

610
std::ostream& operator<<(std::ostream& o, const EbpfMapDescriptor& desc) {
14✔
611
    return o << "(" << "original_fd = " << desc.original_fd << ", " << "inner_map_fd = " << desc.inner_map_fd << ", "
14✔
612
             << "type = " << desc.type << ", " << "max_entries = " << desc.max_entries << ", "
14✔
613
             << "value_size = " << desc.value_size << ", " << "key_size = " << desc.key_size << ")";
14✔
614
}
615

616
void print_map_descriptors(const std::vector<EbpfMapDescriptor>& descriptors, std::ostream& o) {
19✔
617
    int i = 0;
19✔
618
    for (const auto& desc : descriptors) {
33✔
619
        o << "map " << i << ":" << desc << "\n";
14✔
620
        i++;
14✔
621
    }
622
}
19✔
623

624
std::ostream& operator<<(std::ostream& os, const btf_line_info_t& line_info) {
×
625
    os << "; " << line_info.file_name << ":" << line_info.line_number << "\n";
×
626
    os << "; " << line_info.source_line << "\n";
×
627
    return os;
×
628
}
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