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

Add TOTAL_TIME column to PLG$PROF_REQUESTS. Add view PLG$PROF_STATEMENT_STATS_VIEW.

This commit is contained in:
Adriano dos Santos Fernandes 2022-06-10 22:14:36 -03:00
parent 7411434b8b
commit 82b98c07d1
9 changed files with 132 additions and 31 deletions

View File

@ -266,6 +266,7 @@ Below is the list of tables that stores profile data.
- `CALLER_REQUEST_ID` type `BIGINT` - Caller request ID
- `START_TIMESTAMP` type `TIMESTAMP WITH TIME ZONE` - Moment this request was first gathered profile data
- `FINISH_TIMESTAMP` type `TIMESTAMP WITH TIME ZONE` - Moment this request was finished
- `TOTAL_TIME` type `BIGINT` - Accumulated execution time (in nanoseconds) of the request
- Primary key: `PROFILE_ID, REQUEST_ID`
## Table `PLG$PROF_PSQL_STATS`
@ -275,10 +276,10 @@ Below is the list of tables that stores profile data.
- `LINE_NUM` type `INTEGER` - Line number of the statement
- `COLUMN_NUM` type `INTEGER` - Column number of the statement
- `STATEMENT_ID` type `BIGINT` - Statement ID
- `COUNTER` type `BIGINT` - Number of executed times of the statement
- `MIN_TIME` type `BIGINT` - Minimal time (in nanoseconds) of a statement execution
- `MAX_TIME` type `BIGINT` - Maximum time (in nanoseconds) of a statement execution
- `TOTAL_TIME` type `BIGINT` - Accumulated execution time (in nanoseconds) of the statement
- `COUNTER` type `BIGINT` - Number of executed times of the line/column
- `MIN_TIME` type `BIGINT` - Minimal time (in nanoseconds) of a line/column execution
- `MAX_TIME` type `BIGINT` - Maximum time (in nanoseconds) of a line/column execution
- `TOTAL_TIME` type `BIGINT` - Accumulated execution time (in nanoseconds) of the line/column
- Primary key: `PROFILE_ID, REQUEST_ID, LINE_NUM, COLUMN_NUM`
## Table `PLG$PROF_RECORD_SOURCE_STATS`
@ -306,6 +307,44 @@ They should be the preferred way to analyze the collected data. They can also be
After hot spots are found, one can drill down in the data at the request level through the tables.
## View `PLG$PROF_STATEMENT_STATS_VIEW`
```
select req.profile_id,
req.statement_id,
sta.statement_type,
sta.package_name,
sta.routine_name,
sta.parent_statement_id,
sta_parent.statement_type parent_statement_type,
sta_parent.routine_name parent_routine_name,
(select sql_text
from plg$prof_statements
where profile_id = req.profile_id and
statement_id = coalesce(sta.parent_statement_id, req.statement_id)
) sql_text,
count(*) counter,
min(req.total_time) min_time,
max(req.total_time) max_time,
sum(req.total_time) total_time,
sum(req.total_time) / count(*) avg_time
from plg$prof_requests req
join plg$prof_statements sta
on sta.profile_id = req.profile_id and
sta.statement_id = req.statement_id
left join plg$prof_statements sta_parent
on sta_parent.profile_id = sta.profile_id and
sta_parent.statement_id = sta.parent_statement_id
group by req.profile_id,
req.statement_id,
sta.statement_type,
sta.package_name,
sta.routine_name,
sta.parent_statement_id,
sta_parent.statement_type,
sta_parent.routine_name
order by sum(req.total_time) desc
```
## View `PLG$PROF_PSQL_STATS_VIEW`
```
select pstat.profile_id,

View File

@ -1734,7 +1734,7 @@ interface ProfilerSession : Disposable
void onRequestStart(Status status, int64 requestId, int64 statementId, int64 callerRequestId,
ISC_TIMESTAMP_TZ timestamp);
void onRequestFinish(Status status, int64 requestId, ISC_TIMESTAMP_TZ timestamp);
void onRequestFinish(Status status, int64 requestId, ISC_TIMESTAMP_TZ timestamp, uint64 runTime);
void beforePsqlLineColumn(int64 requestId, uint line, uint column);
void afterPsqlLineColumn(int64 requestId, uint line, uint column, uint64 runTime);

View File

@ -6701,7 +6701,7 @@ namespace Firebird
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) throw();
void (CLOOP_CARG *defineRecordSource)(IProfilerSession* self, ISC_INT64 statementId, unsigned cursorId, unsigned recSourceId, const char* accessPath, unsigned parentRecSourceId) throw();
void (CLOOP_CARG *onRequestStart)(IProfilerSession* self, IStatus* status, ISC_INT64 requestId, ISC_INT64 statementId, ISC_INT64 callerRequestId, ISC_TIMESTAMP_TZ timestamp) throw();
void (CLOOP_CARG *onRequestFinish)(IProfilerSession* self, IStatus* status, ISC_INT64 requestId, ISC_TIMESTAMP_TZ timestamp) throw();
void (CLOOP_CARG *onRequestFinish)(IProfilerSession* self, IStatus* status, ISC_INT64 requestId, ISC_TIMESTAMP_TZ timestamp, ISC_UINT64 runTime) throw();
void (CLOOP_CARG *beforePsqlLineColumn)(IProfilerSession* self, ISC_INT64 requestId, unsigned line, unsigned column) throw();
void (CLOOP_CARG *afterPsqlLineColumn)(IProfilerSession* self, ISC_INT64 requestId, unsigned line, unsigned column, ISC_UINT64 runTime) throw();
void (CLOOP_CARG *beforeRecordSourceOpen)(IProfilerSession* self, ISC_INT64 requestId, unsigned cursorId, unsigned recSourceId) throw();
@ -6771,10 +6771,10 @@ namespace Firebird
StatusType::checkException(status);
}
template <typename StatusType> void onRequestFinish(StatusType* status, ISC_INT64 requestId, ISC_TIMESTAMP_TZ timestamp)
template <typename StatusType> void onRequestFinish(StatusType* status, ISC_INT64 requestId, ISC_TIMESTAMP_TZ timestamp, ISC_UINT64 runTime)
{
StatusType::clearException(status);
static_cast<VTable*>(this->cloopVTable)->onRequestFinish(this, status, requestId, timestamp);
static_cast<VTable*>(this->cloopVTable)->onRequestFinish(this, status, requestId, timestamp, runTime);
StatusType::checkException(status);
}
@ -20204,13 +20204,13 @@ namespace Firebird
}
}
static void CLOOP_CARG clooponRequestFinishDispatcher(IProfilerSession* self, IStatus* status, ISC_INT64 requestId, ISC_TIMESTAMP_TZ timestamp) throw()
static void CLOOP_CARG clooponRequestFinishDispatcher(IProfilerSession* self, IStatus* status, ISC_INT64 requestId, ISC_TIMESTAMP_TZ timestamp, ISC_UINT64 runTime) throw()
{
StatusType status2(status);
try
{
static_cast<Name*>(self)->Name::onRequestFinish(&status2, requestId, timestamp);
static_cast<Name*>(self)->Name::onRequestFinish(&status2, requestId, timestamp, runTime);
}
catch (...)
{
@ -20323,7 +20323,7 @@ namespace Firebird
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 defineRecordSource(ISC_INT64 statementId, unsigned cursorId, unsigned recSourceId, const char* accessPath, unsigned parentRecSourceId) = 0;
virtual void onRequestStart(StatusType* status, ISC_INT64 requestId, ISC_INT64 statementId, ISC_INT64 callerRequestId, ISC_TIMESTAMP_TZ timestamp) = 0;
virtual void onRequestFinish(StatusType* status, ISC_INT64 requestId, ISC_TIMESTAMP_TZ timestamp) = 0;
virtual void onRequestFinish(StatusType* status, ISC_INT64 requestId, ISC_TIMESTAMP_TZ timestamp, ISC_UINT64 runTime) = 0;
virtual void beforePsqlLineColumn(ISC_INT64 requestId, unsigned line, unsigned column) = 0;
virtual void afterPsqlLineColumn(ISC_INT64 requestId, unsigned line, unsigned column, ISC_UINT64 runTime) = 0;
virtual void beforeRecordSourceOpen(ISC_INT64 requestId, unsigned cursorId, unsigned recSourceId) = 0;

View File

@ -724,7 +724,7 @@ type
IProfilerSession_defineStatementPtr = procedure(this: IProfilerSession; status: IStatus; statementId: Int64; parentStatementId: Int64; type_: PAnsiChar; packageName: PAnsiChar; routineName: PAnsiChar; sqlText: PAnsiChar); cdecl;
IProfilerSession_defineRecordSourcePtr = procedure(this: IProfilerSession; statementId: Int64; cursorId: Cardinal; recSourceId: Cardinal; accessPath: PAnsiChar; parentRecSourceId: Cardinal); cdecl;
IProfilerSession_onRequestStartPtr = procedure(this: IProfilerSession; status: IStatus; requestId: Int64; statementId: Int64; callerRequestId: Int64; timestamp: ISC_TIMESTAMP_TZ); cdecl;
IProfilerSession_onRequestFinishPtr = procedure(this: IProfilerSession; status: IStatus; requestId: Int64; timestamp: ISC_TIMESTAMP_TZ); cdecl;
IProfilerSession_onRequestFinishPtr = procedure(this: IProfilerSession; status: IStatus; requestId: Int64; timestamp: ISC_TIMESTAMP_TZ; runTime: QWord); cdecl;
IProfilerSession_beforePsqlLineColumnPtr = procedure(this: IProfilerSession; requestId: Int64; line: Cardinal; column: Cardinal); cdecl;
IProfilerSession_afterPsqlLineColumnPtr = procedure(this: IProfilerSession; requestId: Int64; line: Cardinal; column: Cardinal; runTime: QWord); cdecl;
IProfilerSession_beforeRecordSourceOpenPtr = procedure(this: IProfilerSession; requestId: Int64; cursorId: Cardinal; recSourceId: Cardinal); cdecl;
@ -3821,7 +3821,7 @@ type
procedure defineStatement(status: IStatus; statementId: Int64; parentStatementId: Int64; type_: PAnsiChar; packageName: PAnsiChar; routineName: PAnsiChar; sqlText: PAnsiChar);
procedure defineRecordSource(statementId: Int64; cursorId: Cardinal; recSourceId: Cardinal; accessPath: PAnsiChar; parentRecSourceId: Cardinal);
procedure onRequestStart(status: IStatus; requestId: Int64; statementId: Int64; callerRequestId: Int64; timestamp: ISC_TIMESTAMP_TZ);
procedure onRequestFinish(status: IStatus; requestId: Int64; timestamp: ISC_TIMESTAMP_TZ);
procedure onRequestFinish(status: IStatus; requestId: Int64; timestamp: ISC_TIMESTAMP_TZ; runTime: QWord);
procedure beforePsqlLineColumn(requestId: Int64; line: Cardinal; column: Cardinal);
procedure afterPsqlLineColumn(requestId: Int64; line: Cardinal; column: Cardinal; runTime: QWord);
procedure beforeRecordSourceOpen(requestId: Int64; cursorId: Cardinal; recSourceId: Cardinal);
@ -3841,7 +3841,7 @@ type
procedure defineStatement(status: IStatus; statementId: Int64; parentStatementId: Int64; type_: PAnsiChar; packageName: PAnsiChar; routineName: PAnsiChar; sqlText: PAnsiChar); virtual; abstract;
procedure defineRecordSource(statementId: Int64; cursorId: Cardinal; recSourceId: Cardinal; accessPath: PAnsiChar; parentRecSourceId: Cardinal); virtual; abstract;
procedure onRequestStart(status: IStatus; requestId: Int64; statementId: Int64; callerRequestId: Int64; timestamp: ISC_TIMESTAMP_TZ); virtual; abstract;
procedure onRequestFinish(status: IStatus; requestId: Int64; timestamp: ISC_TIMESTAMP_TZ); virtual; abstract;
procedure onRequestFinish(status: IStatus; requestId: Int64; timestamp: ISC_TIMESTAMP_TZ; runTime: QWord); virtual; abstract;
procedure beforePsqlLineColumn(requestId: Int64; line: Cardinal; column: Cardinal); virtual; abstract;
procedure afterPsqlLineColumn(requestId: Int64; line: Cardinal; column: Cardinal; runTime: QWord); virtual; abstract;
procedure beforeRecordSourceOpen(requestId: Int64; cursorId: Cardinal; recSourceId: Cardinal); virtual; abstract;
@ -9002,9 +9002,9 @@ begin
FbException.checkException(status);
end;
procedure IProfilerSession.onRequestFinish(status: IStatus; requestId: Int64; timestamp: ISC_TIMESTAMP_TZ);
procedure IProfilerSession.onRequestFinish(status: IStatus; requestId: Int64; timestamp: ISC_TIMESTAMP_TZ; runTime: QWord);
begin
ProfilerSessionVTable(vTable).onRequestFinish(Self, status, requestId, timestamp);
ProfilerSessionVTable(vTable).onRequestFinish(Self, status, requestId, timestamp, runTime);
FbException.checkException(status);
end;
@ -15683,10 +15683,10 @@ begin
end
end;
procedure IProfilerSessionImpl_onRequestFinishDispatcher(this: IProfilerSession; status: IStatus; requestId: Int64; timestamp: ISC_TIMESTAMP_TZ); cdecl;
procedure IProfilerSessionImpl_onRequestFinishDispatcher(this: IProfilerSession; status: IStatus; requestId: Int64; timestamp: ISC_TIMESTAMP_TZ; runTime: QWord); cdecl;
begin
try
IProfilerSessionImpl(this).onRequestFinish(status, requestId, timestamp);
IProfilerSessionImpl(this).onRequestFinish(status, requestId, timestamp, runTime);
except
on e: Exception do FbException.catchException(status, e);
end

View File

@ -510,14 +510,14 @@ void ProfilerManager::prepareRecSource(thread_db* tdbb, jrd_req* request, const
}
}
void ProfilerManager::onRequestFinish(jrd_req* request)
void ProfilerManager::onRequestFinish(jrd_req* request, FB_UINT64 runTime)
{
if (const auto profileRequestId = getRequest(request, 0))
{
const auto timestamp = TimeZoneUtil::getCurrentTimeStamp(request->req_attachment->att_current_timezone);
LogLocalStatus status("Profiler onRequestFinish");
currentSession->pluginSession->onRequestFinish(&status, profileRequestId, timestamp);
currentSession->pluginSession->onRequestFinish(&status, profileRequestId, timestamp, runTime);
currentSession->requests.findAndRemove(profileRequestId);
}

View File

@ -103,7 +103,7 @@ public:
const Firebird::PathName& pluginName, const Firebird::string& description, const Firebird::string& options);
void prepareRecSource(thread_db* tdbb, jrd_req* request, const RecordSource* rsb);
void onRequestFinish(jrd_req* request);
void onRequestFinish(jrd_req* request, FB_UINT64 runTime);
void beforePsqlLineColumn(jrd_req* request, ULONG line, ULONG column);
void afterPsqlLineColumn(jrd_req* request, ULONG line, ULONG column, FB_UINT64 runTime);
void beforeRecordSourceOpen(jrd_req* request, const RecordSource* rsb);

View File

@ -910,6 +910,8 @@ void EXE_start(thread_db* tdbb, jrd_req* request, jrd_tra* transaction)
request->req_records_affected.clear();
request->req_profiler_time = 0;
// Store request start time for timestamp work
TimeZoneUtil::validateGmtTimeStamp(request->req_gmt_timestamp);
@ -1003,7 +1005,7 @@ void EXE_unwind(thread_db* tdbb, jrd_req* request)
const auto attachment = request->req_attachment;
if (attachment->isProfilerActive() && !request->hasInternalStatement())
attachment->getProfilerManager(tdbb)->onRequestFinish(request);
attachment->getProfilerManager(tdbb)->onRequestFinish(request, request->req_profiler_time);
}
request->req_sorts.unlinkAll();
@ -1359,7 +1361,8 @@ const StmtNode* EXE_looper(thread_db* tdbb, jrd_req* request, const StmtNode* no
// Execute stuff until we drop
SINT64 lastPerfCounter = fb_utils::query_performance_counter();
SINT64 initialPerfCounter = fb_utils::query_performance_counter();
SINT64 lastPerfCounter = initialPerfCounter;
const StmtNode* profileNode = nullptr;
while (node && !(request->req_flags & req_stall))
@ -1444,13 +1447,18 @@ const StmtNode* EXE_looper(thread_db* tdbb, jrd_req* request, const StmtNode* no
}
} // while()
if (attachment->isProfilerActive() && !request->hasInternalStatement() && profileNode)
if (attachment->isProfilerActive() && !request->hasInternalStatement())
{
const SINT64 currentPerfCounter = fb_utils::query_performance_counter();
attachment->getProfilerManager(tdbb)->afterPsqlLineColumn(request,
profileNode->line, profileNode->column,
currentPerfCounter - lastPerfCounter);
if (profileNode)
{
attachment->getProfilerManager(tdbb)->afterPsqlLineColumn(request,
profileNode->line, profileNode->column,
currentPerfCounter - lastPerfCounter);
}
request->req_profiler_time += currentPerfCounter - initialPerfCounter;
}
request->adjustCallerStats();
@ -1477,6 +1485,9 @@ const StmtNode* EXE_looper(thread_db* tdbb, jrd_req* request, const StmtNode* no
request->req_flags &= ~(req_active | req_reserved);
request->req_gmt_timestamp.invalidate();
release_blobs(tdbb, request);
if (attachment->isProfilerActive() && !request->hasInternalStatement())
attachment->getProfilerManager(tdbb)->onRequestFinish(request, request->req_profiler_time);
}
request->req_next = node;

View File

@ -269,6 +269,7 @@ public:
RuntimeStatistics req_stats;
RuntimeStatistics req_base_stats;
AffectedRows req_records_affected; // records affected by the last statement
FB_UINT64 req_profiler_time; // profiler time
const StmtNode* req_next; // next node for execution
EDS::Statement* req_ext_stmt; // head of list of active dynamic statements

View File

@ -119,6 +119,7 @@ struct Request
SINT64 callerRequestId;
ISC_TIMESTAMP_TZ startTimestamp;
Nullable<ISC_TIMESTAMP_TZ> finishTimestamp;
Nullable<FB_UINT64> totalTime;
NonPooledMap<CursorRecSourceKey, RecordSourceStats> recordSourcesStats{defaultPool()};
NonPooledMap<LineColumnKey, Stats> psqlStats{defaultPool()};
};
@ -164,7 +165,8 @@ public:
void onRequestStart(ThrowStatusExceptionWrapper* status, SINT64 requestId, SINT64 statementId,
SINT64 callerRequestId, ISC_TIMESTAMP_TZ timestamp) override;
void onRequestFinish(ThrowStatusExceptionWrapper* status, SINT64 requestId, ISC_TIMESTAMP_TZ timestamp) override;
void onRequestFinish(ThrowStatusExceptionWrapper* status, SINT64 requestId,
ISC_TIMESTAMP_TZ timestamp, FB_UINT64 runTime) override;
void beforePsqlLineColumn(SINT64 requestId, unsigned line, unsigned column) override
{
@ -391,8 +393,8 @@ void ProfilerPlugin::flush(ThrowStatusExceptionWrapper* status)
constexpr auto requestSql = R"""(
update or insert into plg$prof_requests
(profile_id, request_id, statement_id, caller_request_id, start_timestamp, finish_timestamp)
values (?, ?, ?, ?, ?, ?)
(profile_id, request_id, statement_id, caller_request_id, start_timestamp, finish_timestamp, total_time)
values (?, ?, ?, ?, ?, ?, ?)
matching (profile_id, request_id)
)""";
@ -403,6 +405,7 @@ void ProfilerPlugin::flush(ThrowStatusExceptionWrapper* status)
(FB_BIGINT, callerRequestId)
(FB_TIMESTAMP_TZ, startTimestamp)
(FB_TIMESTAMP_TZ, finishTimestamp)
(FB_BIGINT, totalTime)
) requestMessage(status, MasterInterfacePtr());
requestMessage.clear();
@ -732,6 +735,9 @@ void ProfilerPlugin::flush(ThrowStatusExceptionWrapper* status)
requestMessage->finishTimestampNull = profileRequest.finishTimestamp.isUnknown();
requestMessage->finishTimestamp = profileRequest.finishTimestamp.value;
requestMessage->totalTimeNull = profileRequest.totalTime.isUnknown();
requestMessage->totalTime = profileRequest.totalTime.value;
addBatch(requestBatch, requestBatchSize, requestMessage);
if (profileRequest.finishTimestamp.isAssigned())
@ -938,6 +944,7 @@ void ProfilerPlugin::createMetadata(ThrowStatusExceptionWrapper* status, RefPtr<
caller_request_id bigint,
start_timestamp timestamp with time zone not null,
finish_timestamp timestamp with time zone,
total_time bigint,
constraint plg$prof_requests_pk
primary key (profile_id, request_id)
using index plg$prof_requests_profile_request,
@ -1021,6 +1028,47 @@ void ProfilerPlugin::createMetadata(ThrowStatusExceptionWrapper* status, RefPtr<
"grant select, update, insert, delete on table plg$prof_record_source_stats to plg$profiler",
R"""(
create view plg$prof_statement_stats_view
as
select req.profile_id,
req.statement_id,
sta.statement_type,
sta.package_name,
sta.routine_name,
sta.parent_statement_id,
sta_parent.statement_type parent_statement_type,
sta_parent.routine_name parent_routine_name,
(select sql_text
from plg$prof_statements
where profile_id = req.profile_id and
statement_id = coalesce(sta.parent_statement_id, req.statement_id)
) sql_text,
count(*) counter,
min(req.total_time) min_time,
max(req.total_time) max_time,
sum(req.total_time) total_time,
sum(req.total_time) / count(*) avg_time
from plg$prof_requests req
join plg$prof_statements sta
on sta.profile_id = req.profile_id and
sta.statement_id = req.statement_id
left join plg$prof_statements sta_parent
on sta_parent.profile_id = sta.profile_id and
sta_parent.statement_id = sta.parent_statement_id
group by req.profile_id,
req.statement_id,
sta.statement_type,
sta.package_name,
sta.routine_name,
sta.parent_statement_id,
sta_parent.statement_type,
sta_parent.routine_name
order by sum(req.total_time) desc
)""",
"grant select on table plg$prof_statement_stats_view to plg$profiler",
R"""(
create view plg$prof_psql_stats_view
as
@ -1283,12 +1331,14 @@ void Session::onRequestStart(ThrowStatusExceptionWrapper* status, SINT64 request
request->startTimestamp = timestamp;
}
void Session::onRequestFinish(ThrowStatusExceptionWrapper* status, SINT64 requestId, ISC_TIMESTAMP_TZ timestamp)
void Session::onRequestFinish(ThrowStatusExceptionWrapper* status, SINT64 requestId,
ISC_TIMESTAMP_TZ timestamp, FB_UINT64 runTime)
{
if (auto request = requests.get(requestId))
{
request->dirty = true;
request->finishTimestamp = timestamp;
request->totalTime = runTime;
}
}