8
0
mirror of https://github.com/FirebirdSQL/firebird.git synced 2025-01-23 00:03:02 +01:00

Improvement #7687 - Add LEVEL column to PLG$PROF_RECORD_SOURCES and PLG$PROF_RECORD_SOURCE_STATS_VIEW.
All checks were successful
continuous-integration/drone/push Build is passing

Also avoid start collect profiling data for a record source not from its initial node.
This commit is contained in:
Adriano dos Santos Fernandes 2023-07-28 06:43:51 -03:00
parent fc24dfa4be
commit c86bd3d4f4
7 changed files with 113 additions and 66 deletions

View File

@ -265,6 +265,7 @@ Below is the list of tables that stores profile data.
- `CURSOR_ID` type `INTEGER` - Cursor ID - `CURSOR_ID` type `INTEGER` - Cursor ID
- `RECORD_SOURCE_ID` type `INTEGER` - Record source ID - `RECORD_SOURCE_ID` type `INTEGER` - Record source ID
- `PARENT_RECORD_SOURCE_ID` type `INTEGER` - Parent record source ID - `PARENT_RECORD_SOURCE_ID` type `INTEGER` - Parent record source ID
- `LEVEL` type `INTEGER` - Indentation level for the record source
- `ACCESS_PATH` type `VARCHAR(255) CHARACTER SET UTF8` - Access path for the record source - `ACCESS_PATH` type `VARCHAR(255) CHARACTER SET UTF8` - Access path for the record source
- Primary key: `PROFILE_ID, STATEMENT_ID, CURSOR_ID, RECORD_SOURCE_ID` - Primary key: `PROFILE_ID, STATEMENT_ID, CURSOR_ID, RECORD_SOURCE_ID`
@ -419,6 +420,7 @@ select rstat.profile_id,
cur.column_num cursor_column_num, cur.column_num cursor_column_num,
rstat.record_source_id, rstat.record_source_id,
recsrc.parent_record_source_id, recsrc.parent_record_source_id,
recsrc.level,
recsrc.access_path, recsrc.access_path,
cast(sum(rstat.open_counter) as bigint) open_counter, cast(sum(rstat.open_counter) as bigint) open_counter,
min(rstat.open_min_elapsed_time) open_min_elapsed_time, min(rstat.open_min_elapsed_time) open_min_elapsed_time,
@ -461,6 +463,7 @@ select rstat.profile_id,
cur.column_num, cur.column_num,
rstat.record_source_id, rstat.record_source_id,
recsrc.parent_record_source_id, recsrc.parent_record_source_id,
recsrc.level,
recsrc.access_path recsrc.access_path
order by coalesce(sum(rstat.open_total_elapsed_time), 0) + coalesce(sum(rstat.fetch_total_elapsed_time), 0) desc order by coalesce(sum(rstat.open_total_elapsed_time), 0) + coalesce(sum(rstat.fetch_total_elapsed_time), 0) desc
``` ```

View File

@ -1744,7 +1744,7 @@ interface ProfilerSession : Disposable
void defineCursor(int64 statementId, uint cursorId, const string name, uint line, uint column); void defineCursor(int64 statementId, uint cursorId, const string name, uint line, uint column);
void defineRecordSource(int64 statementId, uint cursorId, uint recSourceId, void defineRecordSource(int64 statementId, uint cursorId, uint recSourceId,
const string accessPath, uint parentRecSourceId); uint level, const string accessPath, uint parentRecSourceId);
void onRequestStart(Status status, int64 statementId, int64 requestId, void onRequestStart(Status status, int64 statementId, int64 requestId,
int64 callerStatementId, int64 callerRequestId, ISC_TIMESTAMP_TZ timestamp); int64 callerStatementId, int64 callerRequestId, ISC_TIMESTAMP_TZ timestamp);

View File

@ -6954,7 +6954,7 @@ namespace Firebird
void (CLOOP_CARG *finish)(IProfilerSession* self, IStatus* status, ISC_TIMESTAMP_TZ timestamp) CLOOP_NOEXCEPT; void (CLOOP_CARG *finish)(IProfilerSession* self, IStatus* status, ISC_TIMESTAMP_TZ timestamp) CLOOP_NOEXCEPT;
void (CLOOP_CARG *defineStatement)(IProfilerSession* self, IStatus* status, ISC_INT64 statementId, ISC_INT64 parentStatementId, const char* type, const char* packageName, const char* routineName, const char* sqlText) CLOOP_NOEXCEPT; void (CLOOP_CARG *defineStatement)(IProfilerSession* self, IStatus* status, ISC_INT64 statementId, ISC_INT64 parentStatementId, const char* type, const char* packageName, const char* routineName, const char* sqlText) CLOOP_NOEXCEPT;
void (CLOOP_CARG *defineCursor)(IProfilerSession* self, ISC_INT64 statementId, unsigned cursorId, const char* name, unsigned line, unsigned column) CLOOP_NOEXCEPT; void (CLOOP_CARG *defineCursor)(IProfilerSession* self, ISC_INT64 statementId, unsigned cursorId, const char* name, unsigned line, unsigned column) CLOOP_NOEXCEPT;
void (CLOOP_CARG *defineRecordSource)(IProfilerSession* self, ISC_INT64 statementId, unsigned cursorId, unsigned recSourceId, const char* accessPath, unsigned parentRecSourceId) CLOOP_NOEXCEPT; void (CLOOP_CARG *defineRecordSource)(IProfilerSession* self, ISC_INT64 statementId, unsigned cursorId, unsigned recSourceId, unsigned level, const char* accessPath, unsigned parentRecSourceId) CLOOP_NOEXCEPT;
void (CLOOP_CARG *onRequestStart)(IProfilerSession* self, IStatus* status, ISC_INT64 statementId, ISC_INT64 requestId, ISC_INT64 callerStatementId, ISC_INT64 callerRequestId, ISC_TIMESTAMP_TZ timestamp) CLOOP_NOEXCEPT; void (CLOOP_CARG *onRequestStart)(IProfilerSession* self, IStatus* status, ISC_INT64 statementId, ISC_INT64 requestId, ISC_INT64 callerStatementId, ISC_INT64 callerRequestId, ISC_TIMESTAMP_TZ timestamp) CLOOP_NOEXCEPT;
void (CLOOP_CARG *onRequestFinish)(IProfilerSession* self, IStatus* status, ISC_INT64 statementId, ISC_INT64 requestId, ISC_TIMESTAMP_TZ timestamp, IProfilerStats* stats) CLOOP_NOEXCEPT; void (CLOOP_CARG *onRequestFinish)(IProfilerSession* self, IStatus* status, ISC_INT64 statementId, ISC_INT64 requestId, ISC_TIMESTAMP_TZ timestamp, IProfilerStats* stats) CLOOP_NOEXCEPT;
void (CLOOP_CARG *beforePsqlLineColumn)(IProfilerSession* self, ISC_INT64 statementId, ISC_INT64 requestId, unsigned line, unsigned column) CLOOP_NOEXCEPT; void (CLOOP_CARG *beforePsqlLineColumn)(IProfilerSession* self, ISC_INT64 statementId, ISC_INT64 requestId, unsigned line, unsigned column) CLOOP_NOEXCEPT;
@ -7019,9 +7019,9 @@ namespace Firebird
static_cast<VTable*>(this->cloopVTable)->defineCursor(this, statementId, cursorId, name, line, column); static_cast<VTable*>(this->cloopVTable)->defineCursor(this, statementId, cursorId, name, line, column);
} }
void defineRecordSource(ISC_INT64 statementId, unsigned cursorId, unsigned recSourceId, const char* accessPath, unsigned parentRecSourceId) void defineRecordSource(ISC_INT64 statementId, unsigned cursorId, unsigned recSourceId, unsigned level, const char* accessPath, unsigned parentRecSourceId)
{ {
static_cast<VTable*>(this->cloopVTable)->defineRecordSource(this, statementId, cursorId, recSourceId, accessPath, parentRecSourceId); static_cast<VTable*>(this->cloopVTable)->defineRecordSource(this, statementId, cursorId, recSourceId, level, accessPath, parentRecSourceId);
} }
template <typename StatusType> void onRequestStart(StatusType* status, ISC_INT64 statementId, ISC_INT64 requestId, ISC_INT64 callerStatementId, ISC_INT64 callerRequestId, ISC_TIMESTAMP_TZ timestamp) template <typename StatusType> void onRequestStart(StatusType* status, ISC_INT64 statementId, ISC_INT64 requestId, ISC_INT64 callerStatementId, ISC_INT64 callerRequestId, ISC_TIMESTAMP_TZ timestamp)
@ -20528,11 +20528,11 @@ namespace Firebird
} }
} }
static void CLOOP_CARG cloopdefineRecordSourceDispatcher(IProfilerSession* self, ISC_INT64 statementId, unsigned cursorId, unsigned recSourceId, const char* accessPath, unsigned parentRecSourceId) CLOOP_NOEXCEPT static void CLOOP_CARG cloopdefineRecordSourceDispatcher(IProfilerSession* self, ISC_INT64 statementId, unsigned cursorId, unsigned recSourceId, unsigned level, const char* accessPath, unsigned parentRecSourceId) CLOOP_NOEXCEPT
{ {
try try
{ {
static_cast<Name*>(self)->Name::defineRecordSource(statementId, cursorId, recSourceId, accessPath, parentRecSourceId); static_cast<Name*>(self)->Name::defineRecordSource(statementId, cursorId, recSourceId, level, accessPath, parentRecSourceId);
} }
catch (...) catch (...)
{ {
@ -20672,7 +20672,7 @@ namespace Firebird
virtual void finish(StatusType* status, ISC_TIMESTAMP_TZ timestamp) = 0; virtual void finish(StatusType* status, ISC_TIMESTAMP_TZ timestamp) = 0;
virtual void defineStatement(StatusType* status, ISC_INT64 statementId, ISC_INT64 parentStatementId, const char* type, const char* packageName, const char* routineName, const char* sqlText) = 0; virtual void defineStatement(StatusType* status, ISC_INT64 statementId, ISC_INT64 parentStatementId, const char* type, const char* packageName, const char* routineName, const char* sqlText) = 0;
virtual void defineCursor(ISC_INT64 statementId, unsigned cursorId, const char* name, unsigned line, unsigned column) = 0; virtual void defineCursor(ISC_INT64 statementId, unsigned cursorId, const char* name, unsigned line, unsigned column) = 0;
virtual void defineRecordSource(ISC_INT64 statementId, unsigned cursorId, unsigned recSourceId, const char* accessPath, unsigned parentRecSourceId) = 0; virtual void defineRecordSource(ISC_INT64 statementId, unsigned cursorId, unsigned recSourceId, unsigned level, const char* accessPath, unsigned parentRecSourceId) = 0;
virtual void onRequestStart(StatusType* status, ISC_INT64 statementId, ISC_INT64 requestId, ISC_INT64 callerStatementId, ISC_INT64 callerRequestId, ISC_TIMESTAMP_TZ timestamp) = 0; virtual void onRequestStart(StatusType* status, ISC_INT64 statementId, ISC_INT64 requestId, ISC_INT64 callerStatementId, ISC_INT64 callerRequestId, ISC_TIMESTAMP_TZ timestamp) = 0;
virtual void onRequestFinish(StatusType* status, ISC_INT64 statementId, ISC_INT64 requestId, ISC_TIMESTAMP_TZ timestamp, IProfilerStats* stats) = 0; virtual void onRequestFinish(StatusType* status, ISC_INT64 statementId, ISC_INT64 requestId, ISC_TIMESTAMP_TZ timestamp, IProfilerStats* stats) = 0;
virtual void beforePsqlLineColumn(ISC_INT64 statementId, ISC_INT64 requestId, unsigned line, unsigned column) = 0; virtual void beforePsqlLineColumn(ISC_INT64 statementId, ISC_INT64 requestId, unsigned line, unsigned column) = 0;

View File

@ -727,7 +727,7 @@ type
IProfilerSession_finishPtr = procedure(this: IProfilerSession; status: IStatus; timestamp: ISC_TIMESTAMP_TZ); cdecl; IProfilerSession_finishPtr = procedure(this: IProfilerSession; status: IStatus; timestamp: ISC_TIMESTAMP_TZ); cdecl;
IProfilerSession_defineStatementPtr = procedure(this: IProfilerSession; status: IStatus; statementId: Int64; parentStatementId: Int64; type_: PAnsiChar; packageName: PAnsiChar; routineName: PAnsiChar; sqlText: PAnsiChar); cdecl; IProfilerSession_defineStatementPtr = procedure(this: IProfilerSession; status: IStatus; statementId: Int64; parentStatementId: Int64; type_: PAnsiChar; packageName: PAnsiChar; routineName: PAnsiChar; sqlText: PAnsiChar); cdecl;
IProfilerSession_defineCursorPtr = procedure(this: IProfilerSession; statementId: Int64; cursorId: Cardinal; name: PAnsiChar; line: Cardinal; column: Cardinal); cdecl; IProfilerSession_defineCursorPtr = procedure(this: IProfilerSession; statementId: Int64; cursorId: Cardinal; name: PAnsiChar; line: Cardinal; column: Cardinal); cdecl;
IProfilerSession_defineRecordSourcePtr = procedure(this: IProfilerSession; statementId: Int64; cursorId: Cardinal; recSourceId: Cardinal; accessPath: PAnsiChar; parentRecSourceId: Cardinal); cdecl; IProfilerSession_defineRecordSourcePtr = procedure(this: IProfilerSession; statementId: Int64; cursorId: Cardinal; recSourceId: Cardinal; level: Cardinal; accessPath: PAnsiChar; parentRecSourceId: Cardinal); cdecl;
IProfilerSession_onRequestStartPtr = procedure(this: IProfilerSession; status: IStatus; statementId: Int64; requestId: Int64; callerStatementId: Int64; callerRequestId: Int64; timestamp: ISC_TIMESTAMP_TZ); cdecl; IProfilerSession_onRequestStartPtr = procedure(this: IProfilerSession; status: IStatus; statementId: Int64; requestId: Int64; callerStatementId: Int64; callerRequestId: Int64; timestamp: ISC_TIMESTAMP_TZ); cdecl;
IProfilerSession_onRequestFinishPtr = procedure(this: IProfilerSession; status: IStatus; statementId: Int64; requestId: Int64; timestamp: ISC_TIMESTAMP_TZ; stats: IProfilerStats); cdecl; IProfilerSession_onRequestFinishPtr = procedure(this: IProfilerSession; status: IStatus; statementId: Int64; requestId: Int64; timestamp: ISC_TIMESTAMP_TZ; stats: IProfilerStats); cdecl;
IProfilerSession_beforePsqlLineColumnPtr = procedure(this: IProfilerSession; statementId: Int64; requestId: Int64; line: Cardinal; column: Cardinal); cdecl; IProfilerSession_beforePsqlLineColumnPtr = procedure(this: IProfilerSession; statementId: Int64; requestId: Int64; line: Cardinal; column: Cardinal); cdecl;
@ -3838,7 +3838,7 @@ type
procedure finish(status: IStatus; timestamp: ISC_TIMESTAMP_TZ); procedure finish(status: IStatus; timestamp: ISC_TIMESTAMP_TZ);
procedure defineStatement(status: IStatus; statementId: Int64; parentStatementId: Int64; type_: PAnsiChar; packageName: PAnsiChar; routineName: PAnsiChar; sqlText: PAnsiChar); procedure defineStatement(status: IStatus; statementId: Int64; parentStatementId: Int64; type_: PAnsiChar; packageName: PAnsiChar; routineName: PAnsiChar; sqlText: PAnsiChar);
procedure defineCursor(statementId: Int64; cursorId: Cardinal; name: PAnsiChar; line: Cardinal; column: Cardinal); procedure defineCursor(statementId: Int64; cursorId: Cardinal; name: PAnsiChar; line: Cardinal; column: Cardinal);
procedure defineRecordSource(statementId: Int64; cursorId: Cardinal; recSourceId: Cardinal; accessPath: PAnsiChar; parentRecSourceId: Cardinal); procedure defineRecordSource(statementId: Int64; cursorId: Cardinal; recSourceId: Cardinal; level: Cardinal; accessPath: PAnsiChar; parentRecSourceId: Cardinal);
procedure onRequestStart(status: IStatus; statementId: Int64; requestId: Int64; callerStatementId: Int64; callerRequestId: Int64; timestamp: ISC_TIMESTAMP_TZ); procedure onRequestStart(status: IStatus; statementId: Int64; requestId: Int64; callerStatementId: Int64; callerRequestId: Int64; timestamp: ISC_TIMESTAMP_TZ);
procedure onRequestFinish(status: IStatus; statementId: Int64; requestId: Int64; timestamp: ISC_TIMESTAMP_TZ; stats: IProfilerStats); procedure onRequestFinish(status: IStatus; statementId: Int64; requestId: Int64; timestamp: ISC_TIMESTAMP_TZ; stats: IProfilerStats);
procedure beforePsqlLineColumn(statementId: Int64; requestId: Int64; line: Cardinal; column: Cardinal); procedure beforePsqlLineColumn(statementId: Int64; requestId: Int64; line: Cardinal; column: Cardinal);
@ -3859,7 +3859,7 @@ type
procedure finish(status: IStatus; timestamp: ISC_TIMESTAMP_TZ); virtual; abstract; procedure finish(status: IStatus; timestamp: ISC_TIMESTAMP_TZ); virtual; abstract;
procedure defineStatement(status: IStatus; statementId: Int64; parentStatementId: Int64; type_: PAnsiChar; packageName: PAnsiChar; routineName: PAnsiChar; sqlText: PAnsiChar); virtual; abstract; procedure defineStatement(status: IStatus; statementId: Int64; parentStatementId: Int64; type_: PAnsiChar; packageName: PAnsiChar; routineName: PAnsiChar; sqlText: PAnsiChar); virtual; abstract;
procedure defineCursor(statementId: Int64; cursorId: Cardinal; name: PAnsiChar; line: Cardinal; column: Cardinal); virtual; abstract; procedure defineCursor(statementId: Int64; cursorId: Cardinal; name: PAnsiChar; line: Cardinal; column: Cardinal); virtual; abstract;
procedure defineRecordSource(statementId: Int64; cursorId: Cardinal; recSourceId: Cardinal; accessPath: PAnsiChar; parentRecSourceId: Cardinal); virtual; abstract; procedure defineRecordSource(statementId: Int64; cursorId: Cardinal; recSourceId: Cardinal; level: Cardinal; accessPath: PAnsiChar; parentRecSourceId: Cardinal); virtual; abstract;
procedure onRequestStart(status: IStatus; statementId: Int64; requestId: Int64; callerStatementId: Int64; callerRequestId: Int64; timestamp: ISC_TIMESTAMP_TZ); virtual; abstract; procedure onRequestStart(status: IStatus; statementId: Int64; requestId: Int64; callerStatementId: Int64; callerRequestId: Int64; timestamp: ISC_TIMESTAMP_TZ); virtual; abstract;
procedure onRequestFinish(status: IStatus; statementId: Int64; requestId: Int64; timestamp: ISC_TIMESTAMP_TZ; stats: IProfilerStats); virtual; abstract; procedure onRequestFinish(status: IStatus; statementId: Int64; requestId: Int64; timestamp: ISC_TIMESTAMP_TZ; stats: IProfilerStats); virtual; abstract;
procedure beforePsqlLineColumn(statementId: Int64; requestId: Int64; line: Cardinal; column: Cardinal); virtual; abstract; procedure beforePsqlLineColumn(statementId: Int64; requestId: Int64; line: Cardinal; column: Cardinal); virtual; abstract;
@ -9428,9 +9428,9 @@ begin
ProfilerSessionVTable(vTable).defineCursor(Self, statementId, cursorId, name, line, column); ProfilerSessionVTable(vTable).defineCursor(Self, statementId, cursorId, name, line, column);
end; end;
procedure IProfilerSession.defineRecordSource(statementId: Int64; cursorId: Cardinal; recSourceId: Cardinal; accessPath: PAnsiChar; parentRecSourceId: Cardinal); procedure IProfilerSession.defineRecordSource(statementId: Int64; cursorId: Cardinal; recSourceId: Cardinal; level: Cardinal; accessPath: PAnsiChar; parentRecSourceId: Cardinal);
begin begin
ProfilerSessionVTable(vTable).defineRecordSource(Self, statementId, cursorId, recSourceId, accessPath, parentRecSourceId); ProfilerSessionVTable(vTable).defineRecordSource(Self, statementId, cursorId, recSourceId, level, accessPath, parentRecSourceId);
end; end;
procedure IProfilerSession.onRequestStart(status: IStatus; statementId: Int64; requestId: Int64; callerStatementId: Int64; callerRequestId: Int64; timestamp: ISC_TIMESTAMP_TZ); procedure IProfilerSession.onRequestStart(status: IStatus; statementId: Int64; requestId: Int64; callerStatementId: Int64; callerRequestId: Int64; timestamp: ISC_TIMESTAMP_TZ);
@ -16541,10 +16541,10 @@ begin
end end
end; end;
procedure IProfilerSessionImpl_defineRecordSourceDispatcher(this: IProfilerSession; statementId: Int64; cursorId: Cardinal; recSourceId: Cardinal; accessPath: PAnsiChar; parentRecSourceId: Cardinal); cdecl; procedure IProfilerSessionImpl_defineRecordSourceDispatcher(this: IProfilerSession; statementId: Int64; cursorId: Cardinal; recSourceId: Cardinal; level: Cardinal; accessPath: PAnsiChar; parentRecSourceId: Cardinal); cdecl;
begin begin
try try
IProfilerSessionImpl(this).defineRecordSource(statementId, cursorId, recSourceId, accessPath, parentRecSourceId); IProfilerSessionImpl(this).defineRecordSource(statementId, cursorId, recSourceId, level, accessPath, parentRecSourceId);
except except
on e: Exception do FbException.catchException(nil, e); on e: Exception do FbException.catchException(nil, e);
end end

View File

@ -475,15 +475,19 @@ void ProfilerManager::prepareCursor(thread_db* tdbb, Request* request, const Sel
auto cursorId = select->getCursorId(); auto cursorId = select->getCursorId();
if (profileStatement->definedCursors.exist(cursorId)) if (!profileStatement->definedCursors.exist(cursorId))
return; {
currentSession->pluginSession->defineCursor(profileStatement->id, cursorId, currentSession->pluginSession->defineCursor(profileStatement->id, cursorId,
select->getName().nullStr(), select->getLine(), select->getColumn()); select->getName().nullStr(), select->getLine(), select->getColumn());
profileStatement->definedCursors.add(cursorId); profileStatement->definedCursors.add(cursorId);
} }
const auto recordSource = select->getAccessPath();
prepareRecSource(tdbb, request, recordSource);
}
void ProfilerManager::prepareRecSource(thread_db* tdbb, Request* request, const RecordSource* rsb) void ProfilerManager::prepareRecSource(thread_db* tdbb, Request* request, const RecordSource* rsb)
{ {
auto profileStatement = getStatement(request); auto profileStatement = getStatement(request);
@ -496,35 +500,32 @@ void ProfilerManager::prepareRecSource(thread_db* tdbb, Request* request, const
fb_assert(profileStatement->definedCursors.exist(rsb->getCursorId())); fb_assert(profileStatement->definedCursors.exist(rsb->getCursorId()));
Array<NonPooledPair<const RecordSource*, const RecordSource*>> tree; struct PlanItem : PermanentStorage
tree.add({rsb, nullptr}); {
explicit PlanItem(MemoryPool& p)
for (unsigned pos = 0; pos < tree.getCount(); ++pos) : PermanentStorage(p)
{ {
const auto thisRsb = tree[pos].first;
Array<const RecordSource*> children;
thisRsb->getChildren(children);
unsigned childPos = pos;
for (const auto child : children)
tree.insert(++childPos, {child, thisRsb});
} }
NonPooledMap<ULONG, ULONG> idSequenceMap; const RecordSource* recordSource = nullptr;
auto sequencePtr = profileStatement->cursorNextSequence.getOrPut(rsb->getCursorId()); const RecordSource* parentRecordSource = nullptr;
string accessPath{getPool()};
unsigned level = 0;
};
for (const auto& pair : tree) ObjectsArray<PlanItem> planItems;
planItems.add().recordSource = rsb;
for (unsigned pos = 0; pos < planItems.getCount(); ++pos)
{ {
const auto cursorId = pair.first->getCursorId(); auto& planItem = planItems[pos];
const auto recSourceId = pair.first->getRecSourceId(); const auto thisRsb = planItem.recordSource;
idSequenceMap.put(recSourceId, ++*sequencePtr);
string accessPath; string& accessPath = planItem.accessPath;
pair.first->print(tdbb, accessPath, true, 0, false); thisRsb->print(tdbb, accessPath, true, 0, false);
constexpr auto INDENT_MARKER = "\n "; constexpr auto INDENT_MARKER = "\n ";
constexpr unsigned INDENT_COUNT = 4;
if (accessPath.find(INDENT_MARKER) == 0) if (accessPath.find(INDENT_MARKER) == 0)
{ {
@ -535,13 +536,44 @@ void ProfilerManager::prepareRecSource(thread_db* tdbb, Request* request, const
} while ((pos = accessPath.find(INDENT_MARKER, pos + 1)) != string::npos); } while ((pos = accessPath.find(INDENT_MARKER, pos + 1)) != string::npos);
} }
if (accessPath.hasData() && accessPath[0] == '\n')
accessPath.erase(0, 1);
Array<const RecordSource*> children;
thisRsb->getChildren(children);
unsigned level = planItem.level;
if (const auto lastLinePos = accessPath.find_last_of('\n'); lastLinePos != string::npos)
level += (accessPath.find_first_not_of(' ', lastLinePos + 1) - lastLinePos + 1) / INDENT_COUNT;
unsigned childPos = pos;
for (const auto child : children)
{
auto& inserted = planItems.insert(++childPos);
inserted.recordSource = child;
inserted.parentRecordSource = thisRsb;
inserted.level = level + 1;
}
}
NonPooledMap<ULONG, ULONG> idSequenceMap;
auto sequencePtr = profileStatement->cursorNextSequence.getOrPut(rsb->getCursorId());
for (const auto& planItem : planItems)
{
const auto cursorId = planItem.recordSource->getCursorId();
const auto recSourceId = planItem.recordSource->getRecSourceId();
idSequenceMap.put(recSourceId, ++*sequencePtr);
ULONG parentSequence = 0; ULONG parentSequence = 0;
if (pair.second) if (planItem.parentRecordSource)
parentSequence = *idSequenceMap.get(pair.second->getRecSourceId()); parentSequence = *idSequenceMap.get(planItem.parentRecordSource->getRecSourceId());
currentSession->pluginSession->defineRecordSource(profileStatement->id, cursorId, currentSession->pluginSession->defineRecordSource(profileStatement->id, cursorId,
*sequencePtr, accessPath.c_str(), parentSequence); *sequencePtr, planItem.level, planItem.accessPath.c_str(), parentSequence);
profileStatement->recSourceSequence.put(recSourceId, *sequencePtr); profileStatement->recSourceSequence.put(recSourceId, *sequencePtr);
} }

View File

@ -94,8 +94,6 @@ public:
{ {
lastTicks = profilerManager->queryTicks(); lastTicks = profilerManager->queryTicks();
profilerManager->prepareRecSource(tdbb, request, recordSource);
if (profilerManager->currentSession->flags & Firebird::IProfilerSession::FLAG_BEFORE_EVENTS) if (profilerManager->currentSession->flags & Firebird::IProfilerSession::FLAG_BEFORE_EVENTS)
{ {
if (event == Event::OPEN) if (event == Event::OPEN)
@ -191,7 +189,6 @@ public:
const Firebird::PathName& pluginName, const Firebird::string& description, const Firebird::string& options); const Firebird::PathName& pluginName, const Firebird::string& description, const Firebird::string& options);
void prepareCursor(thread_db* tdbb, Request* request, const Select* select); void prepareCursor(thread_db* tdbb, Request* request, const Select* select);
void prepareRecSource(thread_db* tdbb, Request* request, const RecordSource* rsb);
void onRequestFinish(Request* request, Stats& stats); void onRequestFinish(Request* request, Stats& stats);
void beforePsqlLineColumn(Request* request, ULONG line, ULONG column) void beforePsqlLineColumn(Request* request, ULONG line, ULONG column)
@ -218,52 +215,56 @@ public:
if (const auto profileRequestId = getRequest(request, Firebird::IProfilerSession::FLAG_BEFORE_EVENTS)) if (const auto profileRequestId = getRequest(request, Firebird::IProfilerSession::FLAG_BEFORE_EVENTS))
{ {
const auto profileStatement = getStatement(request); const auto profileStatement = getStatement(request);
const auto sequencePtr = profileStatement->recSourceSequence.get(rsb->getRecSourceId());
fb_assert(sequencePtr);
if (const auto sequencePtr = profileStatement->recSourceSequence.get(rsb->getRecSourceId()))
{
currentSession->pluginSession->beforeRecordSourceOpen( currentSession->pluginSession->beforeRecordSourceOpen(
profileStatement->id, profileRequestId, rsb->getCursorId(), *sequencePtr); profileStatement->id, profileRequestId, rsb->getCursorId(), *sequencePtr);
} }
} }
}
void afterRecordSourceOpen(Request* request, const RecordSource* rsb, Stats& stats) void afterRecordSourceOpen(Request* request, const RecordSource* rsb, Stats& stats)
{ {
if (const auto profileRequestId = getRequest(request, Firebird::IProfilerSession::FLAG_AFTER_EVENTS)) if (const auto profileRequestId = getRequest(request, Firebird::IProfilerSession::FLAG_AFTER_EVENTS))
{ {
const auto profileStatement = getStatement(request); const auto profileStatement = getStatement(request);
const auto sequencePtr = profileStatement->recSourceSequence.get(rsb->getRecSourceId());
fb_assert(sequencePtr);
if (const auto sequencePtr = profileStatement->recSourceSequence.get(rsb->getRecSourceId()))
{
currentSession->pluginSession->afterRecordSourceOpen( currentSession->pluginSession->afterRecordSourceOpen(
profileStatement->id, profileRequestId, rsb->getCursorId(), *sequencePtr, &stats); profileStatement->id, profileRequestId, rsb->getCursorId(), *sequencePtr, &stats);
} }
} }
}
void beforeRecordSourceGetRecord(Request* request, const RecordSource* rsb) void beforeRecordSourceGetRecord(Request* request, const RecordSource* rsb)
{ {
if (const auto profileRequestId = getRequest(request, Firebird::IProfilerSession::FLAG_BEFORE_EVENTS)) if (const auto profileRequestId = getRequest(request, Firebird::IProfilerSession::FLAG_BEFORE_EVENTS))
{ {
const auto profileStatement = getStatement(request); const auto profileStatement = getStatement(request);
const auto sequencePtr = profileStatement->recSourceSequence.get(rsb->getRecSourceId());
fb_assert(sequencePtr);
if (const auto sequencePtr = profileStatement->recSourceSequence.get(rsb->getRecSourceId()))
{
currentSession->pluginSession->beforeRecordSourceGetRecord( currentSession->pluginSession->beforeRecordSourceGetRecord(
profileStatement->id, profileRequestId, rsb->getCursorId(), *sequencePtr); profileStatement->id, profileRequestId, rsb->getCursorId(), *sequencePtr);
} }
} }
}
void afterRecordSourceGetRecord(Request* request, const RecordSource* rsb, Stats& stats) void afterRecordSourceGetRecord(Request* request, const RecordSource* rsb, Stats& stats)
{ {
if (const auto profileRequestId = getRequest(request, Firebird::IProfilerSession::FLAG_AFTER_EVENTS)) if (const auto profileRequestId = getRequest(request, Firebird::IProfilerSession::FLAG_AFTER_EVENTS))
{ {
const auto profileStatement = getStatement(request); const auto profileStatement = getStatement(request);
const auto sequencePtr = profileStatement->recSourceSequence.get(rsb->getRecSourceId());
fb_assert(sequencePtr);
if (const auto sequencePtr = profileStatement->recSourceSequence.get(rsb->getRecSourceId()))
{
currentSession->pluginSession->afterRecordSourceGetRecord( currentSession->pluginSession->afterRecordSourceGetRecord(
profileStatement->id, profileRequestId, rsb->getCursorId(), *sequencePtr, &stats); profileStatement->id, profileRequestId, rsb->getCursorId(), *sequencePtr, &stats);
} }
} }
}
bool isActive() const bool isActive() const
{ {
@ -282,6 +283,8 @@ public:
} }
private: private:
void prepareRecSource(thread_db* tdbb, Request* request, const RecordSource* rsb);
void cancelSession(); void cancelSession();
void finishSession(thread_db* tdbb, bool flushData); void finishSession(thread_db* tdbb, bool flushData);
void pauseSession(bool flushData); void pauseSession(bool flushData);

View File

@ -106,6 +106,7 @@ struct Cursor
struct RecordSource struct RecordSource
{ {
Nullable<ULONG> parentId; Nullable<ULONG> parentId;
unsigned level;
string accessPath{defaultPool()}; string accessPath{defaultPool()};
}; };
@ -181,7 +182,7 @@ public:
void defineCursor(SINT64 statementId, unsigned cursorId, const char* name, unsigned line, unsigned column) override; void defineCursor(SINT64 statementId, unsigned cursorId, const char* name, unsigned line, unsigned column) override;
void defineRecordSource(SINT64 statementId, unsigned cursorId, unsigned recSourceId, void defineRecordSource(SINT64 statementId, unsigned cursorId, unsigned recSourceId,
const char* accessPath, unsigned parentRecordSourceId) override; unsigned level, const char* accessPath, unsigned parentRecordSourceId) override;
void onRequestStart(ThrowStatusExceptionWrapper* status, SINT64 statementId, SINT64 requestId, void onRequestStart(ThrowStatusExceptionWrapper* status, SINT64 statementId, SINT64 requestId,
SINT64 callerStatementId, SINT64 callerRequestId, ISC_TIMESTAMP_TZ timestamp) override; SINT64 callerStatementId, SINT64 callerRequestId, ISC_TIMESTAMP_TZ timestamp) override;
@ -441,8 +442,8 @@ void ProfilerPlugin::flush(ThrowStatusExceptionWrapper* status)
constexpr auto recSrcSql = R"""( constexpr auto recSrcSql = R"""(
update or insert into plg$prof_record_sources update or insert into plg$prof_record_sources
(profile_id, statement_id, cursor_id, record_source_id, (profile_id, statement_id, cursor_id, record_source_id,
parent_record_source_id, access_path) parent_record_source_id, level, access_path)
values (?, ?, ?, ?, ?, ?) values (?, ?, ?, ?, ?, ?, ?)
matching (profile_id, statement_id, cursor_id, record_source_id) matching (profile_id, statement_id, cursor_id, record_source_id)
)"""; )""";
@ -452,6 +453,7 @@ void ProfilerPlugin::flush(ThrowStatusExceptionWrapper* status)
(FB_INTEGER, cursorId) (FB_INTEGER, cursorId)
(FB_INTEGER, recordSourceId) (FB_INTEGER, recordSourceId)
(FB_INTEGER, parentRecordSourceId) (FB_INTEGER, parentRecordSourceId)
(FB_INTEGER, level)
(FB_INTL_VARCHAR(MAX_ACCESS_PATH_CHAR_LEN * 4, CS_UTF8), accessPath) (FB_INTL_VARCHAR(MAX_ACCESS_PATH_CHAR_LEN * 4, CS_UTF8), accessPath)
) recSrcMessage(status, MasterInterfacePtr()); ) recSrcMessage(status, MasterInterfacePtr());
recSrcMessage.clear(); recSrcMessage.clear();
@ -773,6 +775,9 @@ void ProfilerPlugin::flush(ThrowStatusExceptionWrapper* status)
recSrcMessage->parentRecordSourceIdNull = !recSrc.parentId.specified; recSrcMessage->parentRecordSourceIdNull = !recSrc.parentId.specified;
recSrcMessage->parentRecordSourceId = recSrc.parentId.value; recSrcMessage->parentRecordSourceId = recSrc.parentId.value;
recSrcMessage->levelNull = FB_FALSE;
recSrcMessage->level = recSrc.level;
recSrcMessage->accessPathNull = FB_FALSE; recSrcMessage->accessPathNull = FB_FALSE;
recSrcMessage->accessPath.set(recSrc.accessPath.c_str()); recSrcMessage->accessPath.set(recSrc.accessPath.c_str());
@ -1041,6 +1046,7 @@ void ProfilerPlugin::createMetadata(ThrowStatusExceptionWrapper* status, RefPtr<
cursor_id integer not null, cursor_id integer not null,
record_source_id integer not null, record_source_id integer not null,
parent_record_source_id integer, parent_record_source_id integer,
level integer not null,
access_path varchar(255) character set utf8 not null, access_path varchar(255) character set utf8 not null,
constraint plg$prof_record_sources_pk constraint plg$prof_record_sources_pk
primary key (profile_id, statement_id, cursor_id, record_source_id) primary key (profile_id, statement_id, cursor_id, record_source_id)
@ -1276,6 +1282,7 @@ void ProfilerPlugin::createMetadata(ThrowStatusExceptionWrapper* status, RefPtr<
cur.column_num cursor_column_num, cur.column_num cursor_column_num,
rstat.record_source_id, rstat.record_source_id,
recsrc.parent_record_source_id, recsrc.parent_record_source_id,
recsrc.level,
recsrc.access_path, recsrc.access_path,
cast(sum(rstat.open_counter) as bigint) open_counter, cast(sum(rstat.open_counter) as bigint) open_counter,
min(rstat.open_min_elapsed_time) open_min_elapsed_time, min(rstat.open_min_elapsed_time) open_min_elapsed_time,
@ -1318,6 +1325,7 @@ void ProfilerPlugin::createMetadata(ThrowStatusExceptionWrapper* status, RefPtr<
cur.column_num, cur.column_num,
rstat.record_source_id, rstat.record_source_id,
recsrc.parent_record_source_id, recsrc.parent_record_source_id,
recsrc.level,
recsrc.access_path recsrc.access_path
order by coalesce(sum(rstat.open_total_elapsed_time), 0) + coalesce(sum(rstat.fetch_total_elapsed_time), 0) desc order by coalesce(sum(rstat.open_total_elapsed_time), 0) + coalesce(sum(rstat.fetch_total_elapsed_time), 0) desc
)""", )""",
@ -1440,7 +1448,7 @@ void Session::defineCursor(SINT64 statementId, unsigned cursorId, const char* na
} }
void Session::defineRecordSource(SINT64 statementId, unsigned cursorId, unsigned recSourceId, void Session::defineRecordSource(SINT64 statementId, unsigned cursorId, unsigned recSourceId,
const char* accessPath, unsigned parentRecordSourceId) unsigned level, const char* accessPath, unsigned parentRecordSourceId)
{ {
const auto recSource = recordSources.put({{statementId, cursorId}, recSourceId}); const auto recSource = recordSources.put({{statementId, cursorId}, recSourceId});
fb_assert(recSource); fb_assert(recSource);
@ -1448,6 +1456,7 @@ void Session::defineRecordSource(SINT64 statementId, unsigned cursorId, unsigned
if (!recSource) if (!recSource)
return; return;
recSource->level = level;
recSource->accessPath = accessPath; recSource->accessPath = accessPath;
if (unsigned len = recSource->accessPath.length(); len > MAX_ACCESS_PATH_CHAR_LEN) if (unsigned len = recSource->accessPath.length(); len > MAX_ACCESS_PATH_CHAR_LEN)