17 std::ostringstream
os;
40 frameToCallEvent_.clear();
43void CallGraph::ensureRing() {
44 if (slots_.size() != maxEvents_) slots_.resize(maxEvents_);
49 if (
n == maxEvents_ && slots_.size() ==
n)
return;
51 const size_t keep = count_ <
n ? count_ :
n;
52 const size_t drop = count_ - keep;
53 std::vector<TraceEvent> neu(
n);
54 for (
size_t i = 0; i < drop; ++i) retireSlot(physicalIndex(i));
55 for (
size_t i = 0; i < keep; ++i) neu[i] = (*
this)[drop + i];
57 slots_ = std::move(neu);
63void CallGraph::retireSlot(
size_t physical) {
65 if (old.
id == 0)
return;
67 dataDeps_.erase(old.
id);
70 auto fit = lastDef_.find(old.
frameId);
71 if (fit != lastDef_.end()) {
72 auto vit = fit->second.find(old.
name);
73 if (vit != fit->second.end() && vit->second == old.
id) fit->second.erase(vit);
74 if (fit->second.empty()) lastDef_.erase(fit);
79 auto it = frameToCallEvent_.find(old.
frameId);
80 if (it != frameToCallEvent_.end() && it->second == old.
id) frameToCallEvent_.erase(it);
86uint32_t CallGraph::append(
TraceKind kind,
const SourceLoc& loc,
const std::string&
name) {
90 if (count_ < maxEvents_) {
91 phys = physicalIndex(count_);
97 head_ = (head_ + 1) % maxEvents_;
100 TraceEvent& e = slots_[phys];
101 e.id = nextEventId_++;
105 e.frameId = frameStack_.empty() ? 0 : frameStack_.back();
106 e.parentEventId = lastEventId_;
107 if (e.loc.function.empty() && !
name.empty() &&
109 e.loc.function =
name;
116 const std::string
fn = !funcName.empty() ? funcName : loc.
function;
118 const uint32_t frame = nextFrameId_++;
120 frameStack_.push_back(frame);
121 frameToCallEvent_[frame] =
id;
126 const std::string
fn = !funcName.empty() ? funcName : loc.
function;
128 if (!frameStack_.empty()) {
129 newest().
frameId = frameStack_.back();
130 frameStack_.pop_back();
140 if (var.empty())
return 0;
142 const uint32_t frameId = newest().
frameId;
143 lastDef_[frameId][var] =
id;
146 for (uint32_t fid : frameStack_) {
147 if (fid == frameId)
continue;
148 auto fit = lastDef_.find(fid);
149 if (fit != lastDef_.end() && fit->second.count(var)) fit->second[var] =
id;
155 if (var.empty())
return 0;
161void CallGraph::linkData(uint32_t useEventId,
const std::string& var) {
163 if (!use || var.empty())
return;
166 auto fit = lastDef_.find(use->
frameId);
167 if (fit != lastDef_.end()) {
168 auto vit = fit->second.find(var);
169 if (vit != fit->second.end() && vit->second < useEventId && event(vit->second)) {
170 dataDeps_[useEventId].push_back(vit->second);
176 for (
auto it = frameStack_.rbegin(); it != frameStack_.rend(); ++it) {
177 if (*it == use->
frameId)
continue;
178 auto fit2 = lastDef_.find(*it);
179 if (fit2 == lastDef_.end())
continue;
180 auto vit2 = fit2->second.find(var);
181 if (vit2 != fit2->second.end() && vit2->second < useEventId && event(vit2->second)) {
182 dataDeps_[useEventId].push_back(vit2->second);
188 for (
size_t i = count_; i-- > 0;) {
189 const TraceEvent& e = (*this)[i];
190 if (e.id >= useEventId)
continue;
192 dataDeps_[useEventId].push_back(e.id);
204 if (
id == 0 || count_ == 0)
return nullptr;
205 const uint32_t first = (*this)[0].
id;
206 const uint32_t last = (*this)[count_ - 1].id;
207 if (id < first || id > last)
return nullptr;
208 return &(*this)[
static_cast<size_t>(
id - first)];
212 std::vector<CallFrame> out;
213 out.reserve(frameStack_.size());
214 for (uint32_t fid : frameStack_) {
217 auto it = frameToCallEvent_.find(fid);
218 if (it != frameToCallEvent_.end()) {
219 f.callEventId = it->second;
228 std::vector<CallFrame> stack;
229 if (!
event(eventId))
return stack;
230 for (
size_t i = 0; i < count_; ++i) {
232 if (e.
id > eventId)
break;
237 f.callEventId = e.
id;
240 if (!stack.empty()) stack.pop_back();
247 std::vector<std::pair<SourceLoc, SourceLoc>> edges;
248 std::vector<SourceLoc> stack;
249 for (
size_t i = 0; i < count_; ++i) {
252 if (!stack.empty()) edges.emplace_back(stack.back(), e.
loc);
253 stack.push_back(e.
loc);
255 if (!stack.empty()) stack.pop_back();
262 if (
c.eventId != 0 && event(
c.eventId))
return c.eventId;
263 if (
c.loc.empty())
return count_ == 0 ? 0 : (*this)[count_ - 1].id;
265 for (
size_t i = count_; i-- > 0;) {
266 if (
c.loc.matches((*
this)[i].loc))
return (*
this)[i].id;
268 return count_ == 0 ? 0 : (*this)[count_ - 1].id;
271void CallGraph::collectSeeds(
const SliceCriterion&
c, std::vector<uint32_t>& out)
const {
272 const uint32_t
seed = findSeedEvent(
c);
273 if (
seed == 0)
return;
276 const TraceEvent* se = event(
seed);
279 for (
size_t i = 0; i < count_; ++i) {
280 const TraceEvent& e = (*this)[i];
281 if (e.id >
seed)
break;
282 if (!
c.loc.empty() && !
c.loc.matches(e.loc))
continue;
284 if (e.id !=
seed) out.push_back(e.id);
288 if (!
c.variables.empty()) {
289 for (
const auto& var :
c.variables) {
290 for (
size_t i = count_; i-- > 0;) {
291 const TraceEvent& e = (*this)[i];
292 if (e.id >=
seed)
continue;
304 for (
size_t i = 0; i < count_; ++i) {
305 const TraceEvent& e = (*this)[i];
306 if (e.id >
seed)
break;
308 (
c.loc.empty() ||
c.loc.matches(e.loc))) {
317 std::vector<uint32_t> seeds;
318 collectSeeds(criterion, seeds);
320 result.
summary =
"empty slice (no matching events)";
324 const uint32_t primary = findSeedEvent(criterion);
327 std::unordered_set<uint32_t> visited;
328 std::queue<uint32_t> q;
329 for (uint32_t
s : seeds) {
330 if (visited.insert(
s).second) q.push(
s);
333 auto enqueue = [&](uint32_t
id) {
334 if (!
event(
id))
return;
335 if (visited.insert(
id).second) q.push(
id);
339 const uint32_t
id = q.front();
344 auto dit = dataDeps_.find(
id);
345 if (dit != dataDeps_.end()) {
346 for (uint32_t dep : dit->second) {
347 if (!
event(dep))
continue;
360 auto cit = frameToCallEvent_.find(e->
frameId);
361 if (cit != frameToCallEvent_.end()) {
362 enqueue(cit->second);
364 for (
size_t i = 0; i < count_; ++i) {
377 result.
eventIds.assign(visited.begin(), visited.end());
380 std::unordered_set<std::string> seenLoc;
381 for (uint32_t
id : result.
eventIds) {
385 if (seenLoc.insert(key).second) result.
locations.push_back(e->
loc);
388 std::ostringstream
os;
389 os <<
"backward slice: " << result.
eventIds.size() <<
" events, "
391 <<
" data-flow edges, stack depth " << result.
callStack.size();
399 std::ostringstream
os;
400 os <<
"=== Script Error Trace (dynamic slice) ===\n";
401 os <<
"Error: " << errorMessage <<
"\n";
405 for (
size_t i = 0; i < criterion.
variables.size(); ++i) {
411 os <<
"\n-- Call stack --\n";
415 for (
size_t i = 0; i < slice.
callStack.size(); ++i) {
417 os <<
" #" << i <<
' ' <<
f.loc.toString() <<
"\n";
420 os <<
"\n-- Data flow (def → use) --\n";
422 os <<
" (none recorded; feed onDef/onUse or enable local sampling)\n";
424 std::vector<DataFlowEdge> edges = slice.
dataFlow;
425 std::sort(edges.begin(), edges.end(),
427 return a.toEventId > b.toEventId;
429 std::unordered_set<std::string> seen;
431 for (
const auto& edge : edges) {
434 if (!from || !to)
continue;
435 std::ostringstream
line;
437 const std::string
s =
line.str();
438 if (!seen.insert(
s).second)
continue;
439 os <<
" " <<
s <<
"\n";
446 os <<
"\n-- Relevant code (slice) --\n";
448 os <<
" (no locations)\n";
450 for (
const auto& loc : slice.
locations)
os <<
" " << loc.toString() <<
"\n";
SettlementPipeline::Stage fn
const TraceEvent * event(uint32_t id) const
SliceResult sliceBackward(const SliceCriterion &criterion) const
Dynamic backward slice from an error criterion. Follows data dependencies (Use←Def) and control prede...
uint32_t enter(const SourceLoc &loc, const std::string &funcName)
Convenience: Call then Line at the same site.
uint32_t onLine(const SourceLoc &loc)
uint32_t onUse(const SourceLoc &loc, const std::string &var)
std::string formatErrorReport(const std::string &errorMessage, const SliceCriterion &criterion) const
Human-readable report: message + call stack + data-flow + slice locs.
void setMaxEvents(size_t n)
uint32_t onDef(const SourceLoc &loc, const std::string &var)
std::vector< CallFrame > currentStack() const
std::vector< CallFrame > stackAt(uint32_t eventId) const
Stack reconstructed at (or just before) a given event.
std::vector< std::pair< SourceLoc, SourceLoc > > callEdges() const
uint32_t onCall(const SourceLoc &loc, const std::string &funcName={})
uint32_t onReturn(const SourceLoc &loc, const std::string &funcName={})
Criterion for a Weiser-style dynamic backward slice.
std::vector< std::string > variables
std::vector< CallFrame > callStack
std::vector< SourceLoc > locations
std::vector< DataFlowEdge > dataFlow
std::vector< uint32_t > eventIds
Source location in a Squirrel (or synthetic) script.
bool matches(const SourceLoc &o) const
std::string toString() const
One recorded runtime event used by the dynamic slicer.