diff --git a/doc/sql.extensions/README.profiler.md b/doc/sql.extensions/README.profiler.md index 1847a52bc6..0721ef2a07 100644 --- a/doc/sql.extensions/README.profiler.md +++ b/doc/sql.extensions/README.profiler.md @@ -154,6 +154,14 @@ Calling `RDB$PROFILER.FINISH_SESSION(TRUE)` has the same semantics of calling `R Input parameters: - `FLUSH` type `BOOLEAN NOT NULL` +## Procedure `CANCEL_SESSION` + +`RDB$PROFILER.CANCEL_SESSION` cancels the current profiler session. + +All session data present in the profiler plugin is discarded and will not be flushed. + +Data already flushed is not deleted automatically. + ## Procedure `FLUSH` `RDB$PROFILER.FLUSH` updates the snapshot tables with data from the profile sessions in memory. diff --git a/src/common/classes/RefCounted.h b/src/common/classes/RefCounted.h index f9708e91b6..18f25efd7d 100644 --- a/src/common/classes/RefCounted.h +++ b/src/common/classes/RefCounted.h @@ -220,6 +220,11 @@ namespace Firebird return ptr; } + const T* getPtr() const + { + return ptr; + } + protected: T* assign(T* const p) { diff --git a/src/include/firebird/FirebirdInterface.idl b/src/include/firebird/FirebirdInterface.idl index 2e08a64095..3e38edceef 100644 --- a/src/include/firebird/FirebirdInterface.idl +++ b/src/include/firebird/FirebirdInterface.idl @@ -1719,6 +1719,9 @@ interface ProfilerSession : Disposable int64 getId(); uint getFlags(); + // When closing an attachment, the engine is free to dispose the ProfilerPlugin without call this method in advance. + void cancel(Status status); + void finish(Status status, ISC_TIMESTAMP_TZ timestamp); //// FIXME: Add memory stats diff --git a/src/include/firebird/IdlFbInterfaces.h b/src/include/firebird/IdlFbInterfaces.h index 9fd497fcca..9a579a6bdf 100644 --- a/src/include/firebird/IdlFbInterfaces.h +++ b/src/include/firebird/IdlFbInterfaces.h @@ -6696,6 +6696,7 @@ namespace Firebird { ISC_INT64 (CLOOP_CARG *getId)(IProfilerSession* self) throw(); unsigned (CLOOP_CARG *getFlags)(IProfilerSession* self) throw(); + void (CLOOP_CARG *cancel)(IProfilerSession* self, IStatus* status) throw(); void (CLOOP_CARG *finish)(IProfilerSession* self, IStatus* status, ISC_TIMESTAMP_TZ timestamp) throw(); 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(); @@ -6737,6 +6738,13 @@ namespace Firebird return ret; } + template void cancel(StatusType* status) + { + StatusType::clearException(status); + static_cast(this->cloopVTable)->cancel(this, status); + StatusType::checkException(status); + } + template void finish(StatusType* status, ISC_TIMESTAMP_TZ timestamp) { StatusType::clearException(status); @@ -20084,6 +20092,7 @@ namespace Firebird this->dispose = &Name::cloopdisposeDispatcher; this->getId = &Name::cloopgetIdDispatcher; this->getFlags = &Name::cloopgetFlagsDispatcher; + this->cancel = &Name::cloopcancelDispatcher; this->finish = &Name::cloopfinishDispatcher; this->defineStatement = &Name::cloopdefineStatementDispatcher; this->defineRecordSource = &Name::cloopdefineRecordSourceDispatcher; @@ -20127,6 +20136,20 @@ namespace Firebird } } + static void CLOOP_CARG cloopcancelDispatcher(IProfilerSession* self, IStatus* status) throw() + { + StatusType status2(status); + + try + { + static_cast(self)->Name::cancel(&status2); + } + catch (...) + { + StatusType::catchException(&status2); + } + } + static void CLOOP_CARG cloopfinishDispatcher(IProfilerSession* self, IStatus* status, ISC_TIMESTAMP_TZ timestamp) throw() { StatusType status2(status); @@ -20295,6 +20318,7 @@ namespace Firebird virtual ISC_INT64 getId() = 0; virtual unsigned getFlags() = 0; + virtual void cancel(StatusType* status) = 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 defineRecordSource(ISC_INT64 statementId, unsigned cursorId, unsigned recSourceId, const char* accessPath, unsigned parentRecSourceId) = 0; diff --git a/src/include/gen/Firebird.pas b/src/include/gen/Firebird.pas index 019fc696d2..de48309a88 100644 --- a/src/include/gen/Firebird.pas +++ b/src/include/gen/Firebird.pas @@ -719,6 +719,7 @@ type IProfilerPlugin_flushPtr = procedure(this: IProfilerPlugin; status: IStatus; transaction: ITransaction); cdecl; IProfilerSession_getIdPtr = function(this: IProfilerSession): Int64; cdecl; IProfilerSession_getFlagsPtr = function(this: IProfilerSession): Cardinal; cdecl; + IProfilerSession_cancelPtr = procedure(this: IProfilerSession; status: IStatus); 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_defineRecordSourcePtr = procedure(this: IProfilerSession; statementId: Int64; cursorId: Cardinal; recSourceId: Cardinal; accessPath: PAnsiChar; parentRecSourceId: Cardinal); cdecl; @@ -3794,6 +3795,7 @@ type ProfilerSessionVTable = class(DisposableVTable) getId: IProfilerSession_getIdPtr; getFlags: IProfilerSession_getFlagsPtr; + cancel: IProfilerSession_cancelPtr; finish: IProfilerSession_finishPtr; defineStatement: IProfilerSession_defineStatementPtr; defineRecordSource: IProfilerSession_defineRecordSourcePtr; @@ -3814,6 +3816,7 @@ type function getId(): Int64; function getFlags(): Cardinal; + procedure cancel(status: IStatus); 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 defineRecordSource(statementId: Int64; cursorId: Cardinal; recSourceId: Cardinal; accessPath: PAnsiChar; parentRecSourceId: Cardinal); @@ -3833,6 +3836,7 @@ type procedure dispose(); virtual; abstract; function getId(): Int64; virtual; abstract; function getFlags(): Cardinal; virtual; abstract; + procedure cancel(status: IStatus); 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 defineRecordSource(statementId: Int64; cursorId: Cardinal; recSourceId: Cardinal; accessPath: PAnsiChar; parentRecSourceId: Cardinal); virtual; abstract; @@ -8969,6 +8973,12 @@ begin Result := ProfilerSessionVTable(vTable).getFlags(Self); end; +procedure IProfilerSession.cancel(status: IStatus); +begin + ProfilerSessionVTable(vTable).cancel(Self, status); + FbException.checkException(status); +end; + procedure IProfilerSession.finish(status: IStatus; timestamp: ISC_TIMESTAMP_TZ); begin ProfilerSessionVTable(vTable).finish(Self, status, timestamp); @@ -15628,6 +15638,15 @@ begin end end; +procedure IProfilerSessionImpl_cancelDispatcher(this: IProfilerSession; status: IStatus); cdecl; +begin + try + IProfilerSessionImpl(this).cancel(status); + except + on e: Exception do FbException.catchException(status, e); + end +end; + procedure IProfilerSessionImpl_finishDispatcher(this: IProfilerSession; status: IStatus; timestamp: ISC_TIMESTAMP_TZ); cdecl; begin try @@ -16738,6 +16757,7 @@ initialization IProfilerSessionImpl_vTable.dispose := @IProfilerSessionImpl_disposeDispatcher; IProfilerSessionImpl_vTable.getId := @IProfilerSessionImpl_getIdDispatcher; IProfilerSessionImpl_vTable.getFlags := @IProfilerSessionImpl_getFlagsDispatcher; + IProfilerSessionImpl_vTable.cancel := @IProfilerSessionImpl_cancelDispatcher; IProfilerSessionImpl_vTable.finish := @IProfilerSessionImpl_finishDispatcher; IProfilerSessionImpl_vTable.defineStatement := @IProfilerSessionImpl_defineStatementDispatcher; IProfilerSessionImpl_vTable.defineRecordSource := @IProfilerSessionImpl_defineRecordSourceDispatcher; diff --git a/src/jrd/ProfilerManager.cpp b/src/jrd/ProfilerManager.cpp index cca4c4ba39..27faf9e6ea 100644 --- a/src/jrd/ProfilerManager.cpp +++ b/src/jrd/ProfilerManager.cpp @@ -52,6 +52,26 @@ IExternalResultSet* ProfilerPackage::flushProcedure(ThrowStatusExceptionWrapper* return nullptr; } +IExternalResultSet* ProfilerPackage::cancelSessionProcedure(ThrowStatusExceptionWrapper* /*status*/, + IExternalContext* context, const void* in, void* out) +{ + const auto tdbb = JRD_get_thread_data(); + const auto attachment = tdbb->getAttachment(); + const auto transaction = tdbb->getTransaction(); + + const auto profilerManager = attachment->getProfilerManager(tdbb); + + if (profilerManager->currentSession) + { + LogLocalStatus status("Profiler cancelSession"); + + profilerManager->currentSession->pluginSession->cancel(&status); + profilerManager->currentSession = nullptr; + } + + return nullptr; +} + IExternalResultSet* ProfilerPackage::finishSessionProcedure(ThrowStatusExceptionWrapper* /*status*/, IExternalContext* context, const FinishSessionInput::Type* in, void* out) { @@ -456,6 +476,18 @@ ProfilerPackage::ProfilerPackage(MemoryPool& pool) ODS_13_1, // procedures { + SystemProcedure( + pool, + "CANCEL_SESSION", + SystemProcedureFactory(), + prc_executable, + // input parameters + { + }, + // output parameters + { + } + ), SystemProcedure( pool, "FINISH_SESSION", diff --git a/src/jrd/ProfilerManager.h b/src/jrd/ProfilerManager.h index eb9e9b92f6..34b393c2c8 100644 --- a/src/jrd/ProfilerManager.h +++ b/src/jrd/ProfilerManager.h @@ -128,6 +128,11 @@ private: //---------- + static Firebird::IExternalResultSet* cancelSessionProcedure(Firebird::ThrowStatusExceptionWrapper* status, + Firebird::IExternalContext* context, const void* in, void* out); + + //---------- + FB_MESSAGE(FinishSessionInput, Firebird::ThrowStatusExceptionWrapper, (FB_BOOLEAN, flush) ); diff --git a/src/plugins/profiler/Profiler.cpp b/src/plugins/profiler/Profiler.cpp index 64c7813472..ee29d014f1 100644 --- a/src/plugins/profiler/Profiler.cpp +++ b/src/plugins/profiler/Profiler.cpp @@ -151,6 +151,8 @@ public: return FLAG_AFTER_EVENTS; } + void cancel(ThrowStatusExceptionWrapper* status) override; + void finish(ThrowStatusExceptionWrapper* status, ISC_TIMESTAMP_TZ timestamp) override; void defineStatement(ThrowStatusExceptionWrapper* status, SINT64 statementId, SINT64 parentStatementId, @@ -1166,6 +1168,20 @@ Session::Session(ThrowStatusExceptionWrapper* status, ProfilerPlugin* aPlugin, I addRef(); } +void Session::cancel(ThrowStatusExceptionWrapper* status) +{ + for (unsigned sessionIdx = 0; sessionIdx < plugin->sessions.getCount(); ++sessionIdx) + { + const auto& session = plugin->sessions[sessionIdx]; + + if (session.getPtr() == this) + { + plugin->sessions.remove(sessionIdx); + break; + } + } +} + void Session::finish(ThrowStatusExceptionWrapper* status, ISC_TIMESTAMP_TZ timestamp) { dirty = true;