Refactor FileStackNode::printBacktrace from recursive to iterative

This avoids a potential stack overflow for very long backtraces,
or for corrupt object files with cyclic backtraces
This commit is contained in:
Rangi
2025-12-05 15:23:42 -05:00
committed by Rangi
parent c3e245c13e
commit 2c5c453ab8
2 changed files with 62 additions and 56 deletions

View File

@@ -57,44 +57,47 @@ static std::vector<std::string> includePaths = {""}; // -I
static std::deque<std::string> preIncludeNames; // -P static std::deque<std::string> preIncludeNames; // -P
static bool failedOnMissingInclude = false; static bool failedOnMissingInclude = false;
using TraceNode = std::pair<std::string, uint32_t>; void FileStackNode::printBacktrace(uint32_t curLineNo) const {
using TraceItem = std::pair<FileStackNode const *, uint32_t>;
static std::vector<TraceNode> backtrace(FileStackNode const &node, uint32_t curLineNo) { std::vector<TraceItem> items;
if (node.isQuiet && !tracing.loud) { for (TraceItem item{this, curLineNo};;) {
if (node.parent) { auto &[node, itemLineNo] = item;
bool loud = !node->isQuiet || tracing.loud;
if (loud) {
items.emplace_back(node, itemLineNo);
}
if (!node->parent) {
assume(node->type != NODE_REPT && std::holds_alternative<std::string>(node->data));
break;
}
if (loud || node->type != NODE_REPT) {
// Quiet REPT nodes will pass their interior line number up to their parent, // Quiet REPT nodes will pass their interior line number up to their parent,
// which is more precise than the parent's own line number (since that will be // which is more precise than the parent's own line number (since that will be
// the line number of the "REPT?" or "FOR?" itself). // the line number of the "REPT?" or "FOR?" itself).
return backtrace(*node.parent, node.type == NODE_REPT ? curLineNo : node.lineNo); itemLineNo = node->lineNo;
} }
return {}; // LCOV_EXCL_LINE node = &*node->parent;
} }
if (!node.parent) { using TraceNode = std::pair<std::string, uint32_t>;
assume(node.type != NODE_REPT && std::holds_alternative<std::string>(node.data)); std::vector<TraceNode> traceNodes;
return { traceNodes.reserve(items.size());
{node.name(), curLineNo} for (auto &[node, itemLineNo] : reversed(items)) {
}; if (std::holds_alternative<std::vector<uint32_t>>(node->data)) {
}
std::vector<TraceNode> traceNodes = backtrace(*node.parent, node.lineNo);
if (std::holds_alternative<std::vector<uint32_t>>(node.data)) {
assume(!traceNodes.empty()); // REPT nodes use their parent's name assume(!traceNodes.empty()); // REPT nodes use their parent's name
std::string reptName = traceNodes.back().first; std::string reptName = traceNodes.back().first;
if (std::vector<uint32_t> const &nodeIters = node.iters(); !nodeIters.empty()) { if (std::vector<uint32_t> const &nodeIters = node->iters(); !nodeIters.empty()) {
reptName.append(NODE_SEPARATOR REPT_NODE_PREFIX); reptName.append(NODE_SEPARATOR REPT_NODE_PREFIX);
reptName.append(std::to_string(nodeIters.front())); reptName.append(std::to_string(nodeIters.front()));
} }
traceNodes.emplace_back(reptName, curLineNo); traceNodes.emplace_back(reptName, itemLineNo);
} else { } else {
traceNodes.emplace_back(node.name(), curLineNo); traceNodes.emplace_back(node->name(), itemLineNo);
}
} }
return traceNodes;
}
void FileStackNode::printBacktrace(uint32_t curLineNo) const {
trace_PrintBacktrace( trace_PrintBacktrace(
backtrace(*this, curLineNo), traceNodes,
[](TraceNode const &node) { return node.first.c_str(); }, [](TraceNode const &node) { return node.first.c_str(); },
[](TraceNode const &node) { return node.second; } [](TraceNode const &node) { return node.second; }
); );

View File

@@ -12,44 +12,47 @@
#include "link/warning.hpp" #include "link/warning.hpp"
using TraceNode = std::pair<std::string, uint32_t>; void FileStackNode::printBacktrace(uint32_t curLineNo) const {
using TraceItem = std::pair<FileStackNode const *, uint32_t>;
static std::vector<TraceNode> backtrace(FileStackNode const &node, uint32_t curLineNo) { std::vector<TraceItem> items;
if (node.isQuiet && !tracing.loud) { for (TraceItem item{this, curLineNo};;) {
if (node.parent) { auto &[node, itemLineNo] = item;
bool loud = !node->isQuiet || tracing.loud;
if (loud) {
items.emplace_back(node, itemLineNo);
}
if (!node->parent) {
assume(node->type != NODE_REPT && std::holds_alternative<std::string>(node->data));
break;
}
if (loud || node->type != NODE_REPT) {
// Quiet REPT nodes will pass their interior line number up to their parent, // Quiet REPT nodes will pass their interior line number up to their parent,
// which is more precise than the parent's own line number (since that will be // which is more precise than the parent's own line number (since that will be
// the line number of the "REPT?" or "FOR?" itself). // the line number of the "REPT?" or "FOR?" itself).
return backtrace(*node.parent, node.type == NODE_REPT ? curLineNo : node.lineNo); itemLineNo = node->lineNo;
} }
return {}; // LCOV_EXCL_LINE node = &*node->parent;
} }
if (!node.parent) { using TraceNode = std::pair<std::string, uint32_t>;
assume(node.type != NODE_REPT && std::holds_alternative<std::string>(node.data)); std::vector<TraceNode> traceNodes;
return { traceNodes.reserve(items.size());
{node.name(), curLineNo} for (auto &[node, itemLineNo] : reversed(items)) {
}; if (std::holds_alternative<std::vector<uint32_t>>(node->data)) {
}
std::vector<TraceNode> traceNodes = backtrace(*node.parent, node.lineNo);
if (std::holds_alternative<std::vector<uint32_t>>(node.data)) {
assume(!traceNodes.empty()); // REPT nodes use their parent's name assume(!traceNodes.empty()); // REPT nodes use their parent's name
std::string reptName = traceNodes.back().first; std::string reptName = traceNodes.back().first;
if (std::vector<uint32_t> const &nodeIters = node.iters(); !nodeIters.empty()) { if (std::vector<uint32_t> const &nodeIters = node->iters(); !nodeIters.empty()) {
reptName.append(NODE_SEPARATOR REPT_NODE_PREFIX); reptName.append(NODE_SEPARATOR REPT_NODE_PREFIX);
reptName.append(std::to_string(nodeIters.back())); reptName.append(std::to_string(nodeIters.back()));
} }
traceNodes.emplace_back(reptName, curLineNo); traceNodes.emplace_back(reptName, itemLineNo);
} else { } else {
traceNodes.emplace_back(node.name(), curLineNo); traceNodes.emplace_back(node->name(), itemLineNo);
}
} }
return traceNodes;
}
void FileStackNode::printBacktrace(uint32_t curLineNo) const {
trace_PrintBacktrace( trace_PrintBacktrace(
backtrace(*this, curLineNo), traceNodes,
[](TraceNode const &node) { return node.first.c_str(); }, [](TraceNode const &node) { return node.first.c_str(); },
[](TraceNode const &node) { return node.second; } [](TraceNode const &node) { return node.second; }
); );