mirror of
https://github.com/gbdev/rgbds.git
synced 2025-12-06 17:57:48 +00:00
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:
@@ -57,44 +57,47 @@ static std::vector<std::string> includePaths = {""}; // -I
|
||||
static std::deque<std::string> preIncludeNames; // -P
|
||||
static bool failedOnMissingInclude = false;
|
||||
|
||||
using TraceNode = std::pair<std::string, uint32_t>;
|
||||
|
||||
static std::vector<TraceNode> backtrace(FileStackNode const &node, uint32_t curLineNo) {
|
||||
if (node.isQuiet && !tracing.loud) {
|
||||
if (node.parent) {
|
||||
void FileStackNode::printBacktrace(uint32_t curLineNo) const {
|
||||
using TraceItem = std::pair<FileStackNode const *, uint32_t>;
|
||||
std::vector<TraceItem> items;
|
||||
for (TraceItem item{this, curLineNo};;) {
|
||||
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,
|
||||
// which is more precise than the parent's own line number (since that will be
|
||||
// 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) {
|
||||
assume(node.type != NODE_REPT && std::holds_alternative<std::string>(node.data));
|
||||
return {
|
||||
{node.name(), curLineNo}
|
||||
};
|
||||
}
|
||||
|
||||
std::vector<TraceNode> traceNodes = backtrace(*node.parent, node.lineNo);
|
||||
if (std::holds_alternative<std::vector<uint32_t>>(node.data)) {
|
||||
using TraceNode = std::pair<std::string, uint32_t>;
|
||||
std::vector<TraceNode> traceNodes;
|
||||
traceNodes.reserve(items.size());
|
||||
for (auto &[node, itemLineNo] : reversed(items)) {
|
||||
if (std::holds_alternative<std::vector<uint32_t>>(node->data)) {
|
||||
assume(!traceNodes.empty()); // REPT nodes use their parent's name
|
||||
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(std::to_string(nodeIters.front()));
|
||||
}
|
||||
traceNodes.emplace_back(reptName, curLineNo);
|
||||
traceNodes.emplace_back(reptName, itemLineNo);
|
||||
} else {
|
||||
traceNodes.emplace_back(node.name(), curLineNo);
|
||||
traceNodes.emplace_back(node->name(), itemLineNo);
|
||||
}
|
||||
return traceNodes;
|
||||
}
|
||||
|
||||
void FileStackNode::printBacktrace(uint32_t curLineNo) const {
|
||||
trace_PrintBacktrace(
|
||||
backtrace(*this, curLineNo),
|
||||
traceNodes,
|
||||
[](TraceNode const &node) { return node.first.c_str(); },
|
||||
[](TraceNode const &node) { return node.second; }
|
||||
);
|
||||
|
||||
@@ -12,44 +12,47 @@
|
||||
|
||||
#include "link/warning.hpp"
|
||||
|
||||
using TraceNode = std::pair<std::string, uint32_t>;
|
||||
|
||||
static std::vector<TraceNode> backtrace(FileStackNode const &node, uint32_t curLineNo) {
|
||||
if (node.isQuiet && !tracing.loud) {
|
||||
if (node.parent) {
|
||||
void FileStackNode::printBacktrace(uint32_t curLineNo) const {
|
||||
using TraceItem = std::pair<FileStackNode const *, uint32_t>;
|
||||
std::vector<TraceItem> items;
|
||||
for (TraceItem item{this, curLineNo};;) {
|
||||
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,
|
||||
// which is more precise than the parent's own line number (since that will be
|
||||
// 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) {
|
||||
assume(node.type != NODE_REPT && std::holds_alternative<std::string>(node.data));
|
||||
return {
|
||||
{node.name(), curLineNo}
|
||||
};
|
||||
}
|
||||
|
||||
std::vector<TraceNode> traceNodes = backtrace(*node.parent, node.lineNo);
|
||||
if (std::holds_alternative<std::vector<uint32_t>>(node.data)) {
|
||||
using TraceNode = std::pair<std::string, uint32_t>;
|
||||
std::vector<TraceNode> traceNodes;
|
||||
traceNodes.reserve(items.size());
|
||||
for (auto &[node, itemLineNo] : reversed(items)) {
|
||||
if (std::holds_alternative<std::vector<uint32_t>>(node->data)) {
|
||||
assume(!traceNodes.empty()); // REPT nodes use their parent's name
|
||||
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(std::to_string(nodeIters.back()));
|
||||
}
|
||||
traceNodes.emplace_back(reptName, curLineNo);
|
||||
traceNodes.emplace_back(reptName, itemLineNo);
|
||||
} else {
|
||||
traceNodes.emplace_back(node.name(), curLineNo);
|
||||
traceNodes.emplace_back(node->name(), itemLineNo);
|
||||
}
|
||||
return traceNodes;
|
||||
}
|
||||
|
||||
void FileStackNode::printBacktrace(uint32_t curLineNo) const {
|
||||
trace_PrintBacktrace(
|
||||
backtrace(*this, curLineNo),
|
||||
traceNodes,
|
||||
[](TraceNode const &node) { return node.first.c_str(); },
|
||||
[](TraceNode const &node) { return node.second; }
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user