From 0f59b3c360c4717187c4b7124c3feb968bca5d13 Mon Sep 17 00:00:00 2001 From: Adriano dos Santos Fernandes Date: Mon, 19 Oct 2026 22:01:28 -0300 Subject: [PATCH] Fix #9220 - Profiler reports wrong or missing PSQL line statistics --- src/dsql/Nodes.h | 11 ++++++++-- src/dsql/StmtNodes.h | 24 ++++++++++++++++------ src/jrd/exe.cpp | 48 ++++++++++++++++++++++++++------------------ src/jrd/par.cpp | 35 +++++++++++++++++++++++++++++++- 4 files changed, 89 insertions(+), 29 deletions(-) diff --git a/src/dsql/Nodes.h b/src/dsql/Nodes.h index 3ef4be2d590..bba06c46ee0 100644 --- a/src/dsql/Nodes.h +++ b/src/dsql/Nodes.h @@ -1683,9 +1683,16 @@ class StmtNode : public DmlNode return NULL; } - virtual bool isProfileAware() const + enum class ProfileMode : UCHAR { - return true; + NONE, // not profiled (wrapper and declaration nodes) + EVALUATE, // profiled when evaluated + EVALUATE_RETURN // also profiled on req_return/req_sync (loops that fetch there) + }; + + virtual ProfileMode getProfileMode() const + { + return ProfileMode::EVALUATE; } virtual const StmtNode* execute(thread_db* tdbb, Request* request, ExeState* exeState) const = 0; diff --git a/src/dsql/StmtNodes.h b/src/dsql/StmtNodes.h index ab6bf03221d..ae3681acb0c 100644 --- a/src/dsql/StmtNodes.h +++ b/src/dsql/StmtNodes.h @@ -233,9 +233,9 @@ class CompoundStmtNode : public TypedNode void genBlr(DsqlCompilerScratch* dsqlScratch) override; StmtNode* pass1(thread_db* tdbb, CompilerScratch* csb) override; StmtNode* pass2(thread_db* tdbb, CompilerScratch* csb) override; + + ProfileMode getProfileMode() const override + { + return ProfileMode::EVALUATE_RETURN; + } + const StmtNode* execute(thread_db* tdbb, Request* request, ExeState* exeState) const override; bool isWriteLockMode(Request* request) const; diff --git a/src/jrd/exe.cpp b/src/jrd/exe.cpp index 5b1b231adbe..e68a98d85a3 100644 --- a/src/jrd/exe.cpp +++ b/src/jrd/exe.cpp @@ -44,6 +44,7 @@ */ #include "firebird.h" +#include #ifdef TIME_WITH_SYS_TIME # include # include @@ -1770,30 +1771,37 @@ const StmtNode* EXE_looper(thread_db* tdbb, Request* request, const StmtNode* no request->req_src_line = node->line; request->req_src_column = node->column; } + } + + std::optional cachedProfileMode; - if (attachment->isProfilerActive() && !request->hasInternalStatement()) + if (attachment->isProfilerActive() && !request->hasInternalStatement() && + (request->req_operation == Request::req_evaluate || + ((request->req_operation == Request::req_return || + request->req_operation == Request::req_sync) && + *(cachedProfileMode = node->getProfileMode()) == StmtNode::ProfileMode::EVALUATE_RETURN))) + { + if (!profilerInitialTicks) { - if (!profilerInitialTicks) - { - profilerManager = attachment->getProfilerManager(tdbb); - profilerInitialTicks = profilerLastTicks = profilerManager->queryTicks(); - profilerInitialAccumulatedOverhead = profilerLastAccumulatedOverhead = - profilerManager->getAccumulatedOverhead(); - } + profilerManager = attachment->getProfilerManager(tdbb); + profilerInitialTicks = profilerLastTicks = profilerManager->queryTicks(); + profilerInitialAccumulatedOverhead = profilerLastAccumulatedOverhead = + profilerManager->getAccumulatedOverhead(); + } - if (node->hasLineColumn && - node->isProfileAware() && - (exeState.forceProfileNextEvaluate || - !profileNode || - !(node->line == profileNode->line && node->column == profileNode->column))) - { - profilerLastTicks = profilerCallAfterPsqlLineColumn(); - profilerLastAccumulatedOverhead = profilerManager->getAccumulatedOverhead(); - profileNode = node; - exeState.forceProfileNextEvaluate = false; + if (node->hasLineColumn && + (exeState.forceProfileNextEvaluate || + !profileNode || + !(node->line == profileNode->line && node->column == profileNode->column)) && + (cachedProfileMode.has_value() ? *cachedProfileMode : node->getProfileMode()) != + StmtNode::ProfileMode::NONE) + { + profilerLastTicks = profilerCallAfterPsqlLineColumn(); + profilerLastAccumulatedOverhead = profilerManager->getAccumulatedOverhead(); + profileNode = node; + exeState.forceProfileNextEvaluate = false; - profilerManager->beforePsqlLineColumn(request, profileNode->line, profileNode->column); - } + profilerManager->beforePsqlLineColumn(request, profileNode->line, profileNode->column); } } diff --git a/src/jrd/par.cpp b/src/jrd/par.cpp index f8f2b4d6796..10ba77dfabc 100644 --- a/src/jrd/par.cpp +++ b/src/jrd/par.cpp @@ -82,6 +82,7 @@ static void par_error(BlrReader& blrReader, const Arg::StatusVector& v, bool isS static PlanNode* par_plan(thread_db*, CompilerScratch*); static void parseSubRoutines(thread_db* tdbb, CompilerScratch* csb); static void setNodeLineColumn(CompilerScratch* csb, DmlNode* node, ULONG blrOffset); +static void propagateNodeLineColumn(StmtNode* wrapper); static void checkIndexStatus(CompilerScratch* csb, bool isGbak, IndexStatus idx_status, const QualifiedName& name, Cached::Relation* relation); @@ -1759,6 +1760,38 @@ static void setNodeLineColumn(CompilerScratch* csb, DmlNode* node, ULONG blrOffs node->column = i.mbs_src_col; if (node->getKind() == DmlNode::KIND_STATEMENT) - static_cast(node)->hasLineColumn = true; + { + const auto stmt = static_cast(node); + stmt->hasLineColumn = true; + propagateNodeLineColumn(stmt); + } + } +} + +// DSQL may wrap a source statement with BLR nodes that are not profiler aware (e.g. the savepoint +// enclosure and blr_label). The source mapping is then attached to the wrapper, so pass it down +// to the wrapped statements that have no mapping of their own. +static void propagateNodeLineColumn(StmtNode* wrapper) +{ + const auto inherit = [wrapper](StmtNode* child) + { + if (!child || child->hasLineColumn) + return; + + child->line = wrapper->line; + child->column = wrapper->column; + child->hasLineColumn = true; + + propagateNodeLineColumn(child); + }; + + if (const auto compound = nodeAs(wrapper)) + { + for (auto child : compound->statements) + inherit(child); } + else if (const auto label = nodeAs(wrapper)) + inherit(label->statement); + else if (const auto savepoint = nodeAs(wrapper)) + inherit(savepoint->statement); }