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

daisytuner / docc / 26651271267

29 May 2026 05:11PM UTC coverage: 60.767% (-0.1%) from 60.869%
26651271267

push

github

web-flow
Merge pull request #726 from daisytuner/docc-llvm-fixes

Fixed LLVM frontend bugs

133 of 249 new or added lines in 5 files covered. (53.41%)

14 existing lines in 3 files now uncovered.

35323 of 58129 relevant lines covered (60.77%)

10997.13 hits per line

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

72.26
/sdfg/src/visualizer/dot_visualizer.cpp
1
#include "sdfg/visualizer/dot_visualizer.h"
2

3
#include <cstddef>
4
#include <string>
5
#include <utility>
6

7
#include <regex>
8
#include "sdfg/data_flow/access_node.h"
9
#include "sdfg/data_flow/memlet.h"
10
#include "sdfg/structured_control_flow/control_flow_node.h"
11
#include "sdfg/structured_control_flow/sequence.h"
12
#include "sdfg/structured_sdfg.h"
13
#include "sdfg/symbolic/symbolic.h"
14

15
namespace sdfg {
16
namespace visualizer {
17

18
static std::regex dotIdBadChars("[^a-zA-Z0-9_]+");
19

20
static std::string escapeDotId(size_t id, const std::string& prefix = "") { return prefix + std::to_string(id); }
182✔
21

22
static std::string escapeDotId(const std::string& id, const std::string& prefix = "") {
38✔
23
    return prefix + std::regex_replace(id, dotIdBadChars, "_");
38✔
24
}
38✔
25

NEW
26
void DotVisualizer::visualizeSDFG(const SDFG& sdfg) {
×
NEW
27
    this->stream_.clear();
×
NEW
28
    this->stream_ << "digraph SDFG {\n";
×
NEW
29
    this->stream_.setIndent(4);
×
NEW
30
    this->stream_ << "graph [compound=true];" << std::endl << "node [style=filled,fillcolor=white];" << std::endl;
×
31

32
    // State identifier in DOT
NEW
33
    std::unordered_map<size_t, std::string> node_ids;
×
34

35
    // States as nodes
NEW
36
    for (auto& state : sdfg.states()) {
×
NEW
37
        auto id = escapeDotId(state.element_id(), "state_");
×
NEW
38
        this->stream_ << "subgraph cluster_" << id << " {" << std::endl;
×
NEW
39
        this->stream_.setIndent(this->stream_.indent() + 4);
×
NEW
40
        this->stream_ << "style=filled;fillcolor=white;color=black;label=\"State " << state.element_id() << "\";"
×
NEW
41
                      << std::endl
×
NEW
42
                      << id << " [shape=point,style=invis;label=\"\"];" << std::endl;
×
NEW
43
        this->visualizeDataFlowGraph(id, state.dataflow());
×
UNCOV
44
        this->stream_.setIndent(this->stream_.indent() - 4);
×
UNCOV
45
        this->stream_ << "}" << std::endl;
×
NEW
46
        node_ids.insert({state.element_id(), id});
×
UNCOV
47
    }
×
48

49
    // Edges
NEW
50
    for (auto& edge : sdfg.edges()) {
×
NEW
51
        auto& src_id = node_ids.at(edge.src().element_id());
×
NEW
52
        auto& dst_id = node_ids.at(edge.dst().element_id());
×
NEW
53
        this->stream_ << src_id << " -> " << dst_id << " [ltail=cluster_" << src_id << ",lhead=cluster_" << dst_id
×
NEW
54
                      << ",label=\"";
×
55

56
        // Condition
NEW
57
        bool print_condition = !symbolic::eq(edge.condition(), symbolic::__true__());
×
NEW
58
        if (print_condition) {
×
NEW
59
            this->stream_ << edge.condition()->__str__();
×
UNCOV
60
        }
×
61

62
        // Assignments
NEW
63
        if (!edge.assignments().empty()) {
×
NEW
64
            if (print_condition) {
×
NEW
65
                this->stream_ << ",\\n";
×
UNCOV
66
            }
×
NEW
67
            this->stream_ << "{";
×
NEW
68
            bool first = true;
×
NEW
69
            for (auto& [var, expr] : edge.assignments()) {
×
NEW
70
                if (!first) {
×
NEW
71
                    this->stream_ << "; ";
×
UNCOV
72
                }
×
NEW
73
                this->stream_ << var->get_name() << " = " << expr->__str__();
×
NEW
74
                first = false;
×
UNCOV
75
            }
×
NEW
76
            this->stream_ << "}";
×
NEW
77
        }
×
NEW
78
        this->stream_ << "\"];" << std::endl;
×
NEW
79
    }
×
80

NEW
81
    this->stream_.setIndent(0);
×
NEW
82
    this->stream_ << "}" << std::endl;
×
NEW
83
}
×
84

85
void DotVisualizer::visualizeStructuredSDFG(const StructuredSDFG& sdfg) {
19✔
86
    this->stream_.clear();
19✔
87
    this->stream_ << "digraph " << escapeDotId(sdfg.name()) << " {" << std::endl;
19✔
88
    this->stream_.setIndent(4);
19✔
89
    this->stream_ << "graph [compound=true];" << std::endl;
19✔
90
    this->stream_ << "subgraph cluster_" << escapeDotId(sdfg.name()) << " {" << std::endl;
19✔
91
    this->stream_.setIndent(8);
19✔
92
    this->stream_ << "node [style=filled,fillcolor=white];" << std::endl
19✔
93
                  << "style=filled;color=lightblue;label=\"\";" << std::endl;
19✔
94
    this->visualizeSequence(sdfg, sdfg.root());
19✔
95
    this->stream_.setIndent(4);
19✔
96
    this->stream_ << "}" << std::endl;
19✔
97
    this->stream_.setIndent(0);
19✔
98
    this->stream_ << "}" << std::endl;
19✔
99
}
19✔
100

101
void DotVisualizer::visualizeBlock(const StructuredSDFG& sdfg, const structured_control_flow::Block& block) {
27✔
102
    auto id = escapeDotId(block.element_id(), "block_");
27✔
103
    this->stream_ << "subgraph cluster_" << id << " {" << std::endl;
27✔
104
    this->stream_.setIndent(this->stream_.indent() + 4);
27✔
105
    this->stream_ << "style=filled;shape=box;fillcolor=white;color=black;label=\"";
27✔
106
    if (show_block_ids) {
27✔
NEW
107
        this->stream_ << "#" << block.element_id() << " ";
×
UNCOV
108
    }
×
109
    this->stream_ << "\";" << std::endl;
27✔
110
    this->visualizeDataFlowGraph(id, block.dataflow());
27✔
111
    this->stream_.setIndent(this->stream_.indent() - 4);
27✔
112
    this->stream_ << "}" << std::endl;
27✔
113
}
27✔
114

115
void DotVisualizer::visualizeSequence(const StructuredSDFG& sdfg, const structured_control_flow::Sequence& sequence) {
40✔
116
    std::string last_comp_name_tmp, last_comp_name_cluster_tmp;
40✔
117
    for (size_t i = 0; i < sequence.size(); ++i) {
89✔
118
        std::pair<const structured_control_flow::ControlFlowNode&, const structured_control_flow::Transition&> child =
49✔
119
            sequence.at(i);
49✔
120
        this->visualizeNode(sdfg, child.first);
49✔
121
        if ((i > 0) && !last_comp_name_tmp.empty() && !this->last_comp_name_.empty() &&
49✔
122
            last_comp_name_tmp != this->last_comp_name_) {
49✔
123
            this->stream_ << last_comp_name_tmp << " -> " << this->last_comp_name_ << " [";
9✔
124
            if (!last_comp_name_cluster_tmp.empty()) this->stream_ << "ltail=\"" << last_comp_name_cluster_tmp << "\",";
9✔
125
            if (!this->last_comp_name_cluster_.empty())
9✔
126
                this->stream_ << "lhead=\"" << this->last_comp_name_cluster_ << "\",";
6✔
127
            this->stream_ << "minlen=3]"
9✔
128
                          << ";" << std::endl;
9✔
129
        }
9✔
130
        last_comp_name_tmp = this->last_comp_name_;
49✔
131
        last_comp_name_cluster_tmp = this->last_comp_name_cluster_;
49✔
132
    }
49✔
133
}
40✔
134

135
void DotVisualizer::visualizeIfElse(const StructuredSDFG& sdfg, const structured_control_flow::IfElse& if_else) {
3✔
136
    auto id = escapeDotId(if_else.element_id(), "if_");
3✔
137
    this->stream_ << "subgraph cluster_" << id << " {" << std::endl;
3✔
138
    this->stream_.setIndent(this->stream_.indent() + 4);
3✔
139
    this->stream_ << "style=filled;shape=box;fillcolor=white;color=black;label=\"";
3✔
140
    if (show_block_ids) {
3✔
141
        this->stream_ << "#" << if_else.element_id() << " ";
×
142
    }
×
143
    this->stream_ << "if:\";" << std::endl << id << " [shape=point,style=invis,label=\"\"];" << std::endl;
3✔
144
    for (size_t i = 0; i < if_else.size(); ++i) {
9✔
145
        this->stream_ << "subgraph cluster_" << id << "_" << std::to_string(i) << " {" << std::endl;
6✔
146
        this->stream_.setIndent(this->stream_.indent() + 4);
6✔
147
        this->stream_ << "style=filled;shape=box;fillcolor=white;color=black;label=\""
6✔
148
                      << this->expression(if_else.at(i).second->__str__()) << "\";" << std::endl;
6✔
149
        this->visualizeSequence(sdfg, if_else.at(i).first);
6✔
150
        this->stream_.setIndent(this->stream_.indent() - 4);
6✔
151
        this->stream_ << "}" << std::endl;
6✔
152
    }
6✔
153
    this->stream_.setIndent(this->stream_.indent() - 4);
3✔
154
    this->stream_ << "}" << std::endl;
3✔
155
    this->last_comp_name_ = id;
3✔
156
    this->last_comp_name_cluster_ = "cluster_" + id;
3✔
157
}
3✔
158

159
void DotVisualizer::visualizeWhile(const StructuredSDFG& sdfg, const structured_control_flow::While& while_loop) {
2✔
160
    auto id = escapeDotId(while_loop.element_id(), "while_");
2✔
161
    this->stream_ << "subgraph cluster_" << id << " {" << std::endl;
2✔
162
    this->stream_.setIndent(this->stream_.indent() + 4);
2✔
163
    this->stream_ << "style=filled;shape=box;fillcolor=white;color=black;label=\"";
2✔
164
    if (show_block_ids) {
2✔
165
        this->stream_ << "#" << while_loop.element_id() << " ";
×
166
    }
×
167
    this->stream_ << "while:\";" << std::endl << id << " [shape=point,style=invis,label=\"\"];" << std::endl;
2✔
168
    this->visualizeSequence(sdfg, while_loop.root());
2✔
169
    this->stream_.setIndent(this->stream_.indent() - 4);
2✔
170
    this->stream_ << "}" << std::endl;
2✔
171
    this->last_comp_name_ = id;
2✔
172
    this->last_comp_name_cluster_ = "cluster_" + id;
2✔
173
}
2✔
174

175
void DotVisualizer::visualizeFor(const StructuredSDFG& sdfg, const structured_control_flow::For& loop) {
10✔
176
    auto id = escapeDotId(loop.element_id(), "for_");
10✔
177
    this->stream_ << "subgraph cluster_" << id << " {" << std::endl;
10✔
178
    this->stream_.setIndent(this->stream_.indent() + 4);
10✔
179
    this->stream_ << "style=filled;shape=box;fillcolor=white;color=black;label=\"";
10✔
180
    if (show_block_ids) {
10✔
181
        this->stream_ << "#" << loop.element_id() << " ";
×
182
    }
×
183
    this->stream_ << "for: ";
10✔
184
    this->visualizeForBounds(loop.indvar(), loop.init(), loop.condition(), loop.update());
10✔
185
    this->stream_ << "\";" << std::endl << id << " [shape=point,style=invis,label=\"\"];" << std::endl;
10✔
186
    this->visualizeSequence(sdfg, loop.root());
10✔
187
    this->stream_.setIndent(this->stream_.indent() - 4);
10✔
188
    this->stream_ << "}" << std::endl;
10✔
189
    this->last_comp_name_ = id;
10✔
190
    this->last_comp_name_cluster_ = "cluster_" + id;
10✔
191
}
10✔
192

193
void DotVisualizer::visualizeReturn(const StructuredSDFG& sdfg, const structured_control_flow::Return& return_node) {
2✔
194
    auto id = escapeDotId(return_node.element_id(), "return_");
2✔
195
    this->stream_ << id << " [shape=cds,label=\" return " << return_node.data() << "\"];" << std::endl;
2✔
196
    this->last_comp_name_ = id;
2✔
197
    this->last_comp_name_cluster_.clear();
2✔
198
}
2✔
199
void DotVisualizer::visualizeBreak(const StructuredSDFG& sdfg, const structured_control_flow::Break& break_node) {
1✔
200
    auto id = escapeDotId(break_node.element_id(), "break_");
1✔
201
    this->stream_ << id << " [shape=cds,label=\" break  \"];" << std::endl;
1✔
202
    this->last_comp_name_ = id;
1✔
203
    this->last_comp_name_cluster_.clear();
1✔
204
}
1✔
205

206
void DotVisualizer::visualizeContinue(const StructuredSDFG& sdfg, const structured_control_flow::Continue& continue_node) {
1✔
207
    auto id = escapeDotId(continue_node.element_id(), "cont_");
1✔
208
    this->stream_ << id << " [shape=cds,label=\" continue  \"];" << std::endl;
1✔
209
    this->last_comp_name_ = id;
1✔
210
    this->last_comp_name_cluster_.clear();
1✔
211
}
1✔
212

213
void DotVisualizer::visualizeMap(const StructuredSDFG& sdfg, const structured_control_flow::Map& map_node) {
3✔
214
    auto id = escapeDotId(map_node.element_id(), "map_");
3✔
215
    this->stream_ << "subgraph cluster_" << id << " {" << std::endl;
3✔
216
    this->stream_.setIndent(this->stream_.indent() + 4);
3✔
217
    this->stream_ << "style=filled;shape=box;fillcolor=white;color=black;label=\"";
3✔
218
    if (show_block_ids) {
3✔
219
        this->stream_ << "#" << map_node.element_id() << " ";
×
220
    }
×
221
    this->stream_ << "map: ";
3✔
222
    this->visualizeForBounds(map_node.indvar(), map_node.init(), map_node.condition(), map_node.update());
3✔
223

224
    this->stream_ << "\";" << std::endl << id << " [shape=point,style=invis,label=\"\"];" << std::endl;
3✔
225
    this->visualizeSequence(sdfg, map_node.root());
3✔
226
    this->stream_.setIndent(this->stream_.indent() - 4);
3✔
227
    this->stream_ << "}" << std::endl;
3✔
228
    this->last_comp_name_ = id;
3✔
229
    this->last_comp_name_cluster_ = "cluster_" + id;
3✔
230
}
3✔
231

232
void DotVisualizer::visualizeDataFlowGraph(const std::string& id, const data_flow::DataFlowGraph& dfg) {
27✔
233
    this->last_comp_name_cluster_ = "cluster_" + id;
27✔
234
    if (dfg.nodes().empty()) {
27✔
235
        this->stream_ << id << " [shape=point,style=invis,label=\"\"];" << std::endl;
2✔
236
        this->last_comp_name_ = id;
2✔
237
        return;
2✔
238
    }
2✔
239
    this->last_comp_name_.clear();
25✔
240
    std::list<const data_flow::DataFlowNode*> nodes = dfg.topological_sort();
25✔
241
    for (const data_flow::DataFlowNode* node : nodes) {
79✔
242
        std::vector<std::string> in_connectors;
79✔
243
        bool is_access_node = false;
79✔
244
        bool node_will_show_literal_connectors = false;
79✔
245
        auto nodeId = escapeDotId(node->element_id(), "id");
79✔
246
        if (this->last_comp_name_.empty()) {
79✔
247
            this->last_comp_name_ = nodeId;
25✔
248
        }
25✔
249
        if (const data_flow::Tasklet* tasklet = dynamic_cast<const data_flow::Tasklet*>(node)) {
79✔
250
            this->stream_ << nodeId << " [shape=octagon,label=\"" << tasklet->output() << " = ";
26✔
251
            this->visualizeTasklet(*tasklet);
26✔
252
            this->stream_ << "\"];" << std::endl;
26✔
253

254
            in_connectors = tasklet->inputs();
26✔
255
            node_will_show_literal_connectors = true;
26✔
256
        } else if (const data_flow::ConstantNode* constant_node = dynamic_cast<const data_flow::ConstantNode*>(node)) {
53✔
257
            this->stream_ << nodeId << " [";
7✔
258
            this->stream_ << "penwidth=3.0,";
7✔
259
            if (this->sdfg_.is_transient(constant_node->data())) this->stream_ << "style=\"dashed,filled\",";
7✔
260
            this->stream_ << "label=\"" << constant_node->data() << "\"];" << std::endl;
7✔
261
            is_access_node = true;
7✔
262
        } else if (const data_flow::AccessNode* access_node = dynamic_cast<const data_flow::AccessNode*>(node)) {
46✔
263
            this->stream_ << nodeId << " [";
45✔
264
            this->stream_ << "penwidth=3.0,";
45✔
265
            if (this->sdfg_.is_transient(access_node->data())) this->stream_ << "style=\"dashed,filled\",";
45✔
266
            this->stream_ << "label=\"" << access_node->data() << "\"];" << std::endl;
45✔
267
            is_access_node = true;
45✔
268
        } else if (const data_flow::LibraryNode* libnode = dynamic_cast<const data_flow::LibraryNode*>(node)) {
45✔
269
            this->stream_ << nodeId << " [shape=doubleoctagon,label=\"" << libnode->toStr() << "\"];" << std::endl;
1✔
270
            in_connectors = libnode->inputs();
1✔
271
        }
1✔
272

273
        std::unordered_set<std::string> unused_connectors(in_connectors.begin(), in_connectors.end());
79✔
274
        for (const data_flow::Memlet& iedge : dfg.in_edges(*node)) {
79✔
275
            auto& src = iedge.src();
54✔
276
            auto& dst_conn = iedge.dst_conn();
54✔
277
            bool nonexistent_conn = false;
54✔
278

279
            if (!is_access_node) {
54✔
280
                auto it = unused_connectors.find(dst_conn);
27✔
281
                if (it != unused_connectors.end()) {
27✔
282
                    unused_connectors.erase(it); // remove connector from in_connectors, so it is not used again
27✔
283
                } else {
27✔
NEW
284
                    nonexistent_conn = true;
×
NEW
285
                }
×
286
            }
27✔
287

288
            this->stream_ << escapeDotId(src.element_id(), "id") << " -> " << nodeId << " [label=\"   ";
54✔
289
            bool dstIsVoid = dst_conn == "void";
54✔
290
            bool dstIsRef = dst_conn == "ref";
54✔
291
            bool dstIsDeref = dst_conn == "deref";
54✔
292
            auto& src_conn = iedge.src_conn();
54✔
293
            bool srcIsVoid = src_conn == "void";
54✔
294
            bool srcIsDeref = src_conn == "deref";
54✔
295

296
            if (nonexistent_conn) {
54✔
NEW
297
                this->stream_ << "!!"; // this should not happen, but if it does, we can still visualize the memlet
×
NEW
298
            }
×
299

300
            if (dstIsVoid || dstIsRef || dstIsDeref) { // subset applies to dst
54✔
301
                auto& dstVar = dynamic_cast<data_flow::AccessNode const&>(iedge.dst()).data();
27✔
302
                bool subsetOnDst = false;
27✔
303
                if (srcIsDeref && dstIsVoid) { // Pure Store by Memlet definition (Dereference Memlet Store)
27✔
NEW
304
                    auto& subset = iedge.subset();
×
NEW
305
                    if (subset.size() == 1 && symbolic::eq(subset[0], symbolic::integer(0))) {
×
NEW
306
                        this->stream_ << "*" << dstVar; // store to pointer without further address calc
×
NEW
307
                    } else { // fallback, this should not be allowed to happen
×
NEW
308
                        this->stream_ << dstVar; // use access node name instead of connector-name
×
NEW
309
                        subsetOnDst = true;
×
NEW
310
                    }
×
311
                } else if (dstIsVoid) { // computational memlet / output from tasklet / memory store
27✔
312
                    this->stream_ << dstVar; // use access node name instead of connector-name
27✔
313
                    subsetOnDst = true;
27✔
314
                } else {
27✔
NEW
315
                    this->stream_ << dstVar; // use access node name instead of connector-name
×
NEW
316
                }
×
317
                if (subsetOnDst) {
27✔
318
                    this->visualizeSubset(iedge.subset(), &iedge.base_type());
27✔
319
                }
27✔
320
            } else { // dst is a tasklet/library node
27✔
321
                this->stream_ << dst_conn;
27✔
322
            }
27✔
323

324
            this->stream_ << " = ";
54✔
325

326
            if (srcIsVoid || srcIsDeref) { // subset applies to src, could be computational, reference or dereference
54✔
327
                                           // memlet
328
                auto& srcVar = dynamic_cast<data_flow::AccessNode const&>(src).data();
27✔
329
                bool subsetOnSrc = false;
27✔
330
                if (srcIsVoid && dstIsRef) { // reference memlet / address-of / get-element-ptr equivalent
27✔
NEW
331
                    this->stream_ << "&";
×
NEW
332
                    subsetOnSrc = true;
×
333
                } else if (srcIsVoid && dstIsDeref) { // Dereference memlet / load from address
27✔
NEW
334
                    this->stream_ << "*";
×
NEW
335
                    auto& subset = iedge.subset();
×
NEW
336
                    if (subset.size() != 1 && symbolic::eq(subset[0], symbolic::integer(0))) { // does not match memlet
×
337
                                                                                               // definition -> fallback
NEW
338
                        subsetOnSrc = true;
×
NEW
339
                    }
×
340
                } else if (srcIsVoid) {
27✔
341
                    subsetOnSrc = true;
27✔
342
                }
27✔
343
                this->stream_ << srcVar;
27✔
344
                if (subsetOnSrc) {
27✔
345
                    this->visualizeSubset(iedge.subset(), &iedge.base_type());
27✔
346
                }
27✔
347
            } else {
27✔
348
                this->stream_ << src_conn;
27✔
349
            }
27✔
350
            this->stream_ << "   \"];" << std::endl;
54✔
351
        }
54✔
352

353
        if (!node_will_show_literal_connectors) {
79✔
354
            for (uint64_t i = 0; i < in_connectors.size(); ++i) {
53✔
NEW
355
                auto& in_conn = in_connectors[i];
×
NEW
356
                auto it = unused_connectors.find(in_conn);
×
NEW
357
                if (it != unused_connectors.end()) {
×
NEW
358
                    auto literal_id = escapeDotId(node->element_id(), "id") + "_" + escapeDotId(i, "in");
×
NEW
359
                    this->stream_ << literal_id << " [style=\"invis\", label=\"\"];" << std::endl;
×
NEW
360
                    this->stream_ << literal_id << " -> " << nodeId << " [style=\"dotted\", label=\"" << i << ":"
×
NEW
361
                                  << in_conn << "\"]" << ";" << std::endl;
×
NEW
362
                }
×
NEW
363
            }
×
364
        }
53✔
365
    }
79✔
366
}
25✔
367

NEW
368
void DotVisualizer::writeToFile(const Function& sdfg, const std::filesystem::path& file) { writeToFile(sdfg, &file); }
×
369

370
void DotVisualizer::writeToFile(const Function& sdfg, const std::filesystem::path* file) {
1✔
371
    DotVisualizer viz(sdfg);
1✔
372
    viz.visualize();
1✔
373

374
    std::filesystem::path fileName = file ? *file : std::filesystem::path(sdfg.name() + ".dot");
1✔
375

376
    auto parent_path = fileName.parent_path();
1✔
377
    if (!parent_path.empty()) {
1✔
378
        std::filesystem::create_directories(fileName.parent_path());
×
379
    }
×
380

381
    std::ofstream dotOutput(fileName, std::ofstream::out);
1✔
382
    if (!dotOutput.is_open()) {
1✔
383
        std::cerr << "Could not open file " << fileName << " for writing DOT output." << std::endl;
×
384
    }
×
385

386
    dotOutput << viz.getStream().str();
1✔
387
    dotOutput.close();
1✔
388
}
1✔
389

390
} // namespace visualizer
391
} // namespace sdfg
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