From 4fb58153f5ffdf3a92df6d28786999f0ecee45f8 Mon Sep 17 00:00:00 2001 From: Adriano dos Santos Fernandes Date: Sat, 12 Dec 2020 23:39:37 -0300 Subject: [PATCH] Change profile tables to persistent and create indexes. Add RDB$PROFILE_SESSIONS.RDB$ATTACHMENT_ID and RDB$PROFILE_SESSIONS.RDB$USER. --- doc/sql.extensions/README.profiler.md | 12 +- src/jrd/Profiler.cpp | 515 ++++++++++++++------------ src/jrd/Profiler.h | 33 +- src/jrd/constants.h | 1 + src/jrd/idx.h | 27 +- src/jrd/opt.cpp | 7 - src/jrd/relations.h | 10 +- src/jrd/trig.h | 1 + 8 files changed, 320 insertions(+), 286 deletions(-) diff --git a/doc/sql.extensions/README.profiler.md b/doc/sql.extensions/README.profiler.md index cb1baaa9a2..c4320c4e8d 100644 --- a/doc/sql.extensions/README.profiler.md +++ b/doc/sql.extensions/README.profiler.md @@ -100,10 +100,10 @@ select pstat.* Result data for `RDB$PROFILE_SESSIONS`: -| RDB$PROFILE_SESSION_ID | RDB$DESCRIPTION | RDB$TIMESTAMP | -|-----------------------:|-------------------|--------------------------------------------| -| 1 | Profile Session 1 | 2020-09-27 15:45:58.5930 America/Sao_Paulo | -| 2 | Profile Session 2 | 2020-09-27 15:46:00.3000 America/Sao_Paulo | +| RDB$PROFILE_SESSION_ID | RDB$ATTACHMENT_ID | RDB$USER | RDB$DESCRIPTION | RDB$START_TIMESTAMP | RDB$FINISH_TIMESTAP | +|-----------------------:|------------------:|----------|-------------------|--------------------------------------------|--------------------------------------------| +| 1 | 3 | SYSDBA | Profile Session 1 | 2020-09-27 15:45:58.5930 America/Sao_Paulo | 2020-09-27 15:45:59.0200 America/Sao_Paulo | +| 2 | 3 | SYSDBA | Profile Session 2 | 2020-09-27 15:46:00.3000 America/Sao_Paulo | 2020-09-27 15:46:02.0000 America/Sao_Paulo | Result data for `RDB$PROFILE_REQUESTS`: @@ -188,11 +188,13 @@ It also removes finished sessions from engine memory, so if `RDB$PROFILER.PURGE_ # Snapshot system tables -The profile snaphsot tables are like user's global temporary table with `ON COMMIT PRESERVE ROWS`. Data are per attachment and remains there until disconnected or explicitely purged with `RDB$PROFILER.PURGE_SNAPSHOTS`. +Below is the list of system tables that stores profile data. Note that `gbak` does not backup these tables. ## Table `RDB$PROFILE_SESSIONS` - `RDB$PROFILE_SESSION_ID` type `BIGINT` - Profile session ID + - `RDB$ATTACHMENT_ID` type `BIGINT` - Attachment ID + - `RDB$USER` type `CHAR(63) CHARACTER SET UTF8` - User name - `DESCRIPTION` type `VARCHAR(255) CHARACTER SET UTF8` - Description passed in `RDB$PROFILER.START_SESSION` - `RDB$START_TIMESTAMP` type `TIMESTAMP WITH TIME ZONE` - Moment the profile session was started - `RDB$FINISH_TIMESTAMP` type `TIMESTAMP WITH TIME ZONE` - Moment the profile session was finished (NULL when not finished) diff --git a/src/jrd/Profiler.cpp b/src/jrd/Profiler.cpp index 4573ecb3c9..c69224ce6a 100644 --- a/src/jrd/Profiler.cpp +++ b/src/jrd/Profiler.cpp @@ -27,241 +27,13 @@ #include "../jrd/tra.h" #include "../jrd/ids.h" #include "../jrd/recsrc/Cursor.h" +#include "../jrd/dpm_proto.h" +#include "../jrd/met_proto.h" using namespace Jrd; using namespace Firebird; -ProfileSnapshotData::ProfileSnapshotData(thread_db* tdbb) - : SnapshotData(*tdbb->getAttachment()->att_pool), - pool(*tdbb->getAttachment()->att_pool) -{ - allocBuffers(tdbb); -} - -void ProfileSnapshotData::reset(thread_db* tdbb) -{ - clearSnapshot(); - allocBuffers(tdbb); -} - -void ProfileSnapshotData::update(thread_db* tdbb, Profiler* profiler) -{ - const auto sessionBuffer = getData(rel_prof_sessions); - const auto requestBuffer = getData(rel_prof_requests); - const auto recSourceStatsBuffer = getData(rel_prof_recsrc_stats); - const auto statsBuffer = getData(rel_prof_stats); - - const auto sessionRecord = sessionBuffer->getTempRecord(); - const auto requestRecord = requestBuffer->getTempRecord(); - const auto recSourceStatsRecord = recSourceStatsBuffer->getTempRecord(); - const auto statsRecord = statsBuffer->getTempRecord(); - - sessionBuffer->resetCount(profiler->previousSnapshotCounters.sessions); - requestBuffer->resetCount(profiler->previousSnapshotCounters.requests); - recSourceStatsBuffer->resetCount(profiler->previousSnapshotCounters.recSourceStats); - statsBuffer->resetCount(profiler->previousSnapshotCounters.stats); - - auto sessionAccessor = profiler->sessions.accessor(); - - for (bool sessionFound = sessionAccessor.getFirst(); sessionFound;) - { - const SINT64 sessionId = sessionAccessor.current()->first; - const auto& sessionDescription = sessionAccessor.current()->second.description; - const ISC_TIMESTAMP_TZ sessionStartTimeStamp = sessionAccessor.current()->second.startTimeStamp; - const Nullable sessionFinishTimeStamp = sessionAccessor.current()->second.finishTimeStamp; - - sessionRecord->nullify(); - putField(tdbb, sessionRecord, DumpField(f_prof_ses_id, VALUE_INTEGER, sizeof(sessionId), &sessionId)); - - if (sessionDescription.hasData()) - { - putField(tdbb, sessionRecord, DumpField(f_prof_ses_desc, VALUE_STRING, - sessionDescription.length(), sessionDescription.c_str())); - } - - putField(tdbb, sessionRecord, DumpField(f_prof_ses_start_timestamp, VALUE_TIMESTAMP_TZ, - sizeof(sessionStartTimeStamp), &sessionStartTimeStamp)); - - if (sessionFinishTimeStamp.isAssigned()) - { - putField(tdbb, sessionRecord, DumpField(f_prof_ses_finish_timestamp, VALUE_TIMESTAMP_TZ, - sizeof(sessionFinishTimeStamp.value), &sessionFinishTimeStamp.value)); - } - - sessionBuffer->store(sessionRecord); - - for (const auto& requestIt : sessionAccessor.current()->second.requests) - { - const SINT64 requestId = requestIt.first; - const auto& profileRequest = requestIt.second; - const ISC_TIMESTAMP_TZ requestTimeStamp = profileRequest.timeStamp; - - requestRecord->nullify(); - putField(tdbb, requestRecord, DumpField(f_prof_req_ses_id, VALUE_INTEGER, sizeof(sessionId), &sessionId)); - putField(tdbb, requestRecord, DumpField(f_prof_req_time, VALUE_TIMESTAMP_TZ, - sizeof(requestTimeStamp), &requestTimeStamp)); - putField(tdbb, requestRecord, DumpField(f_prof_req_req_id, VALUE_INTEGER, sizeof(requestId), &requestId)); - putField(tdbb, requestRecord, DumpField(f_prof_req_type, VALUE_STRING, - profileRequest.requestType.length(), profileRequest.requestType.c_str())); - - if (profileRequest.packageName.hasData()) - { - putField(tdbb, requestRecord, DumpField(f_prof_req_pkg_name, VALUE_STRING, - profileRequest.packageName.length(), profileRequest.packageName.c_str())); - } - - if (profileRequest.routineName.hasData()) - { - putField(tdbb, requestRecord, DumpField(f_prof_req_routine, VALUE_STRING, - profileRequest.routineName.length(), profileRequest.routineName.c_str())); - } - - if (profileRequest.sqlText.hasData()) - { - putField(tdbb, requestRecord, DumpField(f_prof_req_sql_text, VALUE_STRING, - profileRequest.sqlText.length(), profileRequest.sqlText.c_str())); - } - - requestBuffer->store(requestRecord); - - for (const auto& cursorIt : profileRequest.cursors) - { - const SINT64 cursorId = cursorIt.first; - const auto& profileCursor = cursorIt.second; - - for (const auto& sourceIt : profileCursor.sources) - { - const SINT64 recSourceId = sourceIt.second.sequence; // use sequence instead of the internal ID - const SINT64 sourceParentId = sourceIt.second.parentSequence.value; - const SINT64 openCounter = sourceIt.second.openStats.counter; - const SINT64 openMinTime = sourceIt.second.openStats.minTime; - const SINT64 openMaxTime = sourceIt.second.openStats.maxTime; - const SINT64 openTotalTime = sourceIt.second.openStats.totalTime; - const SINT64 fetchCounter = sourceIt.second.fetchStats.counter; - const SINT64 fetchMinTime = sourceIt.second.fetchStats.minTime; - const SINT64 fetchMaxTime = sourceIt.second.fetchStats.maxTime; - const SINT64 fetchTotalTime = sourceIt.second.fetchStats.totalTime; - - recSourceStatsRecord->nullify(); - - putField(tdbb, recSourceStatsRecord, - DumpField(f_prof_recsrc_stats_ses_id, VALUE_INTEGER, sizeof(sessionId), &sessionId)); - putField(tdbb, recSourceStatsRecord, - DumpField(f_prof_recsrc_stats_req_id, VALUE_INTEGER, sizeof(requestId), &requestId)); - putField(tdbb, recSourceStatsRecord, - DumpField(f_prof_recsrc_stats_cursor_id, VALUE_INTEGER, sizeof(cursorId), &cursorId)); - putField(tdbb, recSourceStatsRecord, - DumpField(f_prof_recsrc_stats_recsrc_id, VALUE_INTEGER, sizeof(recSourceId), &recSourceId)); - - if (sourceIt.second.parentSequence.specified) - { - putField(tdbb, recSourceStatsRecord, - DumpField(f_prof_recsrc_stats_parent_recsrc_id, VALUE_INTEGER, - sizeof(sourceParentId), &sourceParentId)); - } - - putField(tdbb, recSourceStatsRecord, DumpField(f_prof_recsrc_stats_access_path, VALUE_STRING, - sourceIt.second.accessPath.length(), sourceIt.second.accessPath.c_str())); - - putField(tdbb, recSourceStatsRecord, - DumpField(f_prof_recsrc_stats_open_counter, VALUE_INTEGER, - sizeof(openCounter), &openCounter)); - - putField(tdbb, recSourceStatsRecord, - DumpField(f_prof_recsrc_stats_open_min_time, VALUE_INTEGER, - sizeof(openMinTime), &openMinTime)); - - putField(tdbb, recSourceStatsRecord, - DumpField(f_prof_recsrc_stats_open_max_time, VALUE_INTEGER, - sizeof(openMaxTime), &openMaxTime)); - - putField(tdbb, recSourceStatsRecord, - DumpField(f_prof_recsrc_stats_open_total_time, VALUE_INTEGER, - sizeof(openTotalTime), &openTotalTime)); - - putField(tdbb, recSourceStatsRecord, - DumpField(f_prof_recsrc_stats_fetch_counter, VALUE_INTEGER, - sizeof(fetchCounter), &fetchCounter)); - - putField(tdbb, recSourceStatsRecord, - DumpField(f_prof_recsrc_stats_fetch_min_time, VALUE_INTEGER, - sizeof(fetchMinTime), &fetchMinTime)); - - putField(tdbb, recSourceStatsRecord, - DumpField(f_prof_recsrc_stats_fetch_max_time, VALUE_INTEGER, - sizeof(fetchMaxTime), &fetchMaxTime)); - - putField(tdbb, recSourceStatsRecord, - DumpField(f_prof_recsrc_stats_fetch_total_time, VALUE_INTEGER, - sizeof(fetchTotalTime), &fetchTotalTime)); - - recSourceStatsBuffer->store(recSourceStatsRecord); - } - } - - for (const auto& statsIt : profileRequest.stats) - { - const auto lineColumn = Profiler::decodeLineColumn(statsIt.first); - const SINT64 line = lineColumn.first; - const SINT64 column = lineColumn.second; - const SINT64 counter = statsIt.second.counter; - const SINT64 minTime = statsIt.second.minTime; - const SINT64 maxTime = statsIt.second.maxTime; - const SINT64 totalTime = statsIt.second.totalTime; - - statsRecord->nullify(); - putField(tdbb, statsRecord, DumpField(f_prof_stats_ses_id, VALUE_INTEGER, sizeof(sessionId), &sessionId)); - putField(tdbb, statsRecord, DumpField(f_prof_stats_req_id, VALUE_INTEGER, sizeof(requestId), &requestId)); - putField(tdbb, statsRecord, DumpField(f_prof_stats_line, VALUE_INTEGER, sizeof(line), &line)); - putField(tdbb, statsRecord, DumpField(f_prof_stats_column, VALUE_INTEGER, sizeof(column), &column)); - putField(tdbb, statsRecord, DumpField(f_prof_stats_counter, VALUE_INTEGER, sizeof(counter), &counter)); - putField(tdbb, statsRecord, DumpField(f_prof_stats_min_time, VALUE_INTEGER, sizeof(minTime), &minTime)); - putField(tdbb, statsRecord, DumpField(f_prof_stats_max_time, VALUE_INTEGER, sizeof(maxTime), &maxTime)); - putField(tdbb, statsRecord, DumpField(f_prof_stats_total_time, VALUE_INTEGER, sizeof(totalTime), &totalTime)); - statsBuffer->store(statsRecord); - } - } - - if (sessionId != profiler->currentSessionId) - { - profiler->previousSnapshotCounters.sessions = sessionBuffer->getCount(); - profiler->previousSnapshotCounters.requests = requestBuffer->getCount(); - profiler->previousSnapshotCounters.stats = statsBuffer->getCount(); - - sessionFound = sessionAccessor.fastRemove(); - } - else - sessionFound = sessionAccessor.getNext(); - } -} - -void ProfileSnapshotData::allocBuffers(thread_db* tdbb) -{ - allocBuffer(tdbb, pool, rel_prof_sessions); - allocBuffer(tdbb, pool, rel_prof_requests); - allocBuffer(tdbb, pool, rel_prof_stats); - allocBuffer(tdbb, pool, rel_prof_recsrc_stats); -} - - -//-------------------------------------- - - -const Format* ProfileTableScan::getFormat(thread_db* tdbb, jrd_rel* relation) const -{ - const auto profiler = tdbb->getAttachment()->getProfiler(tdbb); - return profiler->snapshotData.getData(relation)->getFormat(); -} - -bool ProfileTableScan::retrieveRecord(thread_db* tdbb, jrd_rel* relation, - FB_UINT64 position, Record* record) const -{ - const auto profiler = tdbb->getAttachment()->getProfiler(tdbb); - return profiler->snapshotData.getData(relation)->fetch(position, record); -} - - //-------------------------------------- @@ -275,7 +47,7 @@ IExternalResultSet* ProfilerPackage::updateSnapshotProcedure(ThrowStatusExceptio const auto profiler = attachment->getProfiler(tdbb); - profiler->snapshotData.update(tdbb, profiler); + profiler->updateSnapshot(tdbb); return nullptr; } @@ -301,7 +73,7 @@ IExternalResultSet* ProfilerPackage::finishSessionProcedure(ThrowStatusException } if (in->updateSnapshot) - profiler->snapshotData.update(tdbb, profiler); + profiler->updateSnapshot(tdbb); return nullptr; } @@ -322,7 +94,7 @@ IExternalResultSet* ProfilerPackage::pauseSessionProcedure(ThrowStatusExceptionW profiler->paused = true; if (in->updateSnapshot) - profiler->snapshotData.update(tdbb, profiler); + profiler->updateSnapshot(tdbb); return nullptr; } @@ -337,8 +109,21 @@ IExternalResultSet* ProfilerPackage::purgeSnapshotsProcedure(ThrowStatusExceptio const auto profiler = attachment->getProfiler(tdbb); - profiler->snapshotData.reset(tdbb); - profiler->previousSnapshotCounters = {}; + IAttachment* const attachmentIntf = attachment->getInterface(); + ITransaction* const transactionIntf = tdbb->getTransaction()->getInterface(false); + ThrowLocalStatus throwStatus; + + const char* purgeSql = + "execute block as\n" + "begin\n" + " delete from rdb$profile_stats;\n" + " delete from rdb$profile_record_source_stats;\n" + " delete from rdb$profile_requests;\n" + " delete from rdb$profile_sessions;\n" + "end"; + + attachmentIntf->execute(&throwStatus, transactionIntf, 0, purgeSql, SQL_DIALECT_CURRENT, + nullptr, nullptr, nullptr, nullptr); auto sessionAccessor = profiler->sessions.accessor(); @@ -384,7 +169,8 @@ void ProfilerPackage::startSessionFunction(ThrowStatusExceptionWrapper* status, profiler->activeSession = true; profiler->paused = false; - const auto sessionId = ++profiler->currentSessionId; + const auto generator = MET_lookup_generator(tdbb, PROFILE_SESSION_GENERATOR); + const auto sessionId = profiler->currentSessionId = DPM_gen_id(tdbb, generator, false, 1); profiler->sessions.put(sessionId)->init(attachment, in->descriptionNull ? "" : string(string(in->description.str, in->description.length))); @@ -416,8 +202,7 @@ void Profiler::Session::init(Attachment* attachment, const string& aDescription) Profiler::Profiler(thread_db* tdbb) - : sessions(*tdbb->getAttachment()->att_pool), - snapshotData(tdbb) + : sessions(*tdbb->getAttachment()->att_pool) { } @@ -537,6 +322,260 @@ void Profiler::hitRecSourceGetRecord(jrd_req* request, ULONG recSourceId, SINT64 profileRecSource->fetchStats.hit(runTime); } +void Profiler::updateSnapshot(thread_db* tdbb) +{ + const static UCHAR TEMP_BPB[] = {isc_bpb_version1, isc_bpb_storage, 1, isc_bpb_storage_temp}; + + IAttachment* const attachment = tdbb->getAttachment()->getInterface(); + ITransaction* const transaction = tdbb->getTransaction()->getInterface(false); + ThrowLocalStatus throwStatus; + + const char* sessionSql = + "update or insert into rdb$profile_sessions\n" + " (rdb$profile_session_id, rdb$attachment_id, rdb$user, rdb$description, rdb$start_timestamp, rdb$finish_timestamp)\n" + " values (?, ?, ?, ?, ?, ?)\n" + " matching (rdb$profile_session_id)"; + + const char* requestSql = + "update or insert into rdb$profile_requests\n" + " (rdb$profile_session_id, rdb$profile_request_id, rdb$timestamp, rdb$request_type, rdb$package_name,\n" + " rdb$routine_name, rdb$sql_text)\n" + " values (?, ?, ?, ?, ?, ?, ?)\n" + " matching (rdb$profile_session_id, rdb$profile_request_id)"; + + const char* recSrcStatsSql = + "update or insert into rdb$profile_record_source_stats\n" + " (rdb$profile_session_id, rdb$profile_request_id, rdb$cursor_id, rdb$record_source_id,\n" + " rdb$parent_record_source_id, rdb$access_path,\n" + " rdb$open_counter, rdb$open_min_time, rdb$open_max_time, rdb$open_total_time,\n" + " rdb$fetch_counter, rdb$fetch_min_time, rdb$fetch_max_time, rdb$fetch_total_time)\n" + " values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)\n" + " matching (rdb$profile_session_id, rdb$profile_request_id, rdb$cursor_id, rdb$record_source_id)"; + + const char* statsSql = + "update or insert into rdb$profile_stats\n" + " (rdb$profile_session_id, rdb$profile_request_id, rdb$line, rdb$column,\n" + " rdb$counter, rdb$min_time, rdb$max_time, rdb$total_time)\n" + " values (?, ?, ?, ?, ?, ?, ?, ?)\n" + " matching (rdb$profile_session_id, rdb$profile_request_id, rdb$line, rdb$column)"; + + FB_MESSAGE(SessionMessage, ThrowWrapper, + (FB_BIGINT, sessionId) + (FB_BIGINT, attachmentId) + (FB_INTL_VARCHAR(METADATA_IDENTIFIER_CHAR_LEN * 4, CS_UTF8), user) + (FB_INTL_VARCHAR(255 * 4, CS_UTF8), description) + (FB_TIMESTAMP_TZ, startTimestamp) + (FB_TIMESTAMP_TZ, finishTimestamp) + ) sessionMessage(&throwStatus, fb_get_master_interface()); + sessionMessage.clear(); + + FB_MESSAGE(RequestMessage, ThrowWrapper, + (FB_BIGINT, sessionId) + (FB_BIGINT, requestId) + (FB_TIMESTAMP_TZ, timestamp) + (FB_INTL_VARCHAR(20 * 4, CS_UTF8), requestType) + (FB_INTL_VARCHAR(METADATA_IDENTIFIER_CHAR_LEN * 4, CS_UTF8), packageName) + (FB_INTL_VARCHAR(METADATA_IDENTIFIER_CHAR_LEN * 4, CS_UTF8), routineName) + (FB_BLOB, sqlText) + ) requestMessage(&throwStatus, fb_get_master_interface()); + requestMessage.clear(); + + FB_MESSAGE(RecSrcStatsMessage, ThrowWrapper, + (FB_BIGINT, sessionId) + (FB_BIGINT, requestId) + (FB_INTEGER, cursorId) + (FB_INTEGER, recordSourceId) + (FB_BIGINT, parentRecordSourceId) + (FB_INTL_VARCHAR(255 * 4, CS_UTF8), accessPath) + (FB_BIGINT, openCounter) + (FB_BIGINT, openMinTime) + (FB_BIGINT, openMaxTime) + (FB_BIGINT, openTotalTime) + (FB_BIGINT, fetchCounter) + (FB_BIGINT, fetchMinTime) + (FB_BIGINT, fetchMaxTime) + (FB_BIGINT, fetchTotalTime) + ) recSrcStatsMessage(&throwStatus, fb_get_master_interface()); + recSrcStatsMessage.clear(); + + FB_MESSAGE(StatsMessage, ThrowWrapper, + (FB_BIGINT, sessionId) + (FB_BIGINT, requestId) + (FB_INTEGER, line) + (FB_INTEGER, column) + (FB_BIGINT, counter) + (FB_BIGINT, minTime) + (FB_BIGINT, maxTime) + (FB_BIGINT, totalTime) + ) statsMessage(&throwStatus, fb_get_master_interface()); + statsMessage.clear(); + + auto sessionStmt = makeNoIncRef(attachment->prepare( + &throwStatus, transaction, 0, sessionSql, SQL_DIALECT_CURRENT, 0)); + auto requestStmt = makeNoIncRef(attachment->prepare( + &throwStatus, transaction, 0, requestSql, SQL_DIALECT_CURRENT, 0)); + auto recSrcStatsStmt = makeNoIncRef(attachment->prepare( + &throwStatus, transaction, 0, recSrcStatsSql, SQL_DIALECT_CURRENT, 0)); + auto statsStmt = makeNoIncRef(attachment->prepare( + &throwStatus, transaction, 0, statsSql, SQL_DIALECT_CURRENT, 0)); + + auto sessionAccessor = sessions.accessor(); + + for (bool sessionFound = sessionAccessor.getFirst(); sessionFound;) + { + sessionMessage->sessionIdNull = FB_FALSE; + sessionMessage->sessionId = sessionAccessor.current()->first; + + sessionMessage->attachmentIdNull = FB_FALSE; + sessionMessage->attachmentId = tdbb->getAttachment()->att_attachment_id; + + sessionMessage->userNull = FB_FALSE; + sessionMessage->user.set(tdbb->getAttachment()->att_user->getUserName().c_str()); + + sessionMessage->descriptionNull = sessionAccessor.current()->second.description.isEmpty(); + sessionMessage->description.set(sessionAccessor.current()->second.description.c_str()); + + sessionMessage->startTimestampNull = FB_FALSE; + sessionMessage->startTimestamp = sessionAccessor.current()->second.startTimeStamp; + + sessionMessage->finishTimestampNull = sessionAccessor.current()->second.finishTimeStamp.isUnknown(); + sessionMessage->finishTimestamp = sessionAccessor.current()->second.finishTimeStamp.value; + + sessionStmt->execute(&throwStatus, transaction, sessionMessage.getMetadata(), + sessionMessage.getData(), nullptr, nullptr); + + for (const auto& requestIt : sessionAccessor.current()->second.requests) + { + const auto& profileRequest = requestIt.second; + + requestMessage->sessionIdNull = FB_FALSE; + requestMessage->sessionId = sessionMessage->sessionId; + + requestMessage->requestIdNull = FB_FALSE; + requestMessage->requestId = requestIt.first; + + requestMessage->timestampNull = FB_FALSE; + requestMessage->timestamp = profileRequest.timeStamp; + + requestMessage->requestTypeNull = FB_FALSE; + requestMessage->requestType.set(profileRequest.requestType.c_str()); + + requestMessage->packageNameNull = profileRequest.packageName.isEmpty(); + requestMessage->packageName.set(profileRequest.packageName.c_str()); + + requestMessage->routineNameNull = profileRequest.routineName.isEmpty(); + requestMessage->routineName.set(profileRequest.routineName.c_str()); + + requestMessage->sqlTextNull = profileRequest.sqlText.isEmpty(); + + if (profileRequest.sqlText.hasData()) + { + const auto blob = attachment->createBlob( + &throwStatus, transaction, &requestMessage->sqlText, sizeof(TEMP_BPB), TEMP_BPB); + blob->putSegment(&throwStatus, profileRequest.sqlText.length(), profileRequest.sqlText.c_str()); + blob->close(&throwStatus); + } + + requestStmt->execute(&throwStatus, transaction, requestMessage.getMetadata(), + requestMessage.getData(), nullptr, nullptr); + + for (const auto& cursorIt : profileRequest.cursors) + { + const auto& profileCursor = cursorIt.second; + + recSrcStatsMessage->sessionIdNull = FB_FALSE; + recSrcStatsMessage->sessionId = sessionMessage->sessionId; + + recSrcStatsMessage->requestIdNull = FB_FALSE; + recSrcStatsMessage->requestId = requestIt.first; + + recSrcStatsMessage->cursorIdNull = FB_FALSE; + recSrcStatsMessage->cursorId = cursorIt.first; + + for (const auto& sourceIt : profileCursor.sources) + { + const auto& recSrc = sourceIt.second; + + // Use sequence instead of the internal ID. + recSrcStatsMessage->recordSourceIdNull = FB_FALSE; + recSrcStatsMessage->recordSourceId = recSrc.sequence; + + // Use parent sequence instead of the internal parent ID. + recSrcStatsMessage->parentRecordSourceIdNull = !recSrc.parentSequence.specified; + recSrcStatsMessage->parentRecordSourceId = recSrc.parentSequence.value; + + recSrcStatsMessage->accessPathNull = recSrc.accessPath.isEmpty(); + recSrcStatsMessage->accessPath.set(recSrc.accessPath.c_str()); + + recSrcStatsMessage->openCounterNull = FB_FALSE; + recSrcStatsMessage->openCounter = recSrc.openStats.counter; + + recSrcStatsMessage->openMinTimeNull = FB_FALSE; + recSrcStatsMessage->openMinTime = recSrc.openStats.minTime; + + recSrcStatsMessage->openMaxTimeNull = FB_FALSE; + recSrcStatsMessage->openMaxTime = recSrc.openStats.maxTime; + + recSrcStatsMessage->openTotalTimeNull = FB_FALSE; + recSrcStatsMessage->openTotalTime = recSrc.openStats.totalTime; + + recSrcStatsMessage->fetchCounterNull = FB_FALSE; + recSrcStatsMessage->fetchCounter = recSrc.fetchStats.counter; + + recSrcStatsMessage->fetchMinTimeNull = FB_FALSE; + recSrcStatsMessage->fetchMinTime = recSrc.fetchStats.minTime; + + recSrcStatsMessage->fetchMaxTimeNull = FB_FALSE; + recSrcStatsMessage->fetchMaxTime = recSrc.fetchStats.maxTime; + + recSrcStatsMessage->fetchTotalTimeNull = FB_FALSE; + recSrcStatsMessage->fetchTotalTime = recSrc.fetchStats.totalTime; + + recSrcStatsStmt->execute(&throwStatus, transaction, recSrcStatsMessage.getMetadata(), + recSrcStatsMessage.getData(), nullptr, nullptr); + } + } + + for (const auto& statsIt : profileRequest.stats) + { + const auto lineColumn = Profiler::decodeLineColumn(statsIt.first); + + statsMessage->sessionIdNull = FB_FALSE; + statsMessage->sessionId = sessionMessage->sessionId; + + statsMessage->requestIdNull = FB_FALSE; + statsMessage->requestId = requestIt.first; + + statsMessage->lineNull = FB_FALSE; + statsMessage->line = lineColumn.first; + + statsMessage->columnNull = FB_FALSE; + statsMessage->column = lineColumn.second; + + statsMessage->counterNull = FB_FALSE; + statsMessage->counter = statsIt.second.counter; + + statsMessage->minTimeNull = FB_FALSE; + statsMessage->minTime = statsIt.second.minTime; + + statsMessage->maxTimeNull = FB_FALSE; + statsMessage->maxTime = statsIt.second.maxTime; + + statsMessage->totalTimeNull = FB_FALSE; + statsMessage->totalTime = statsIt.second.totalTime; + + statsStmt->execute(&throwStatus, transaction, statsMessage.getMetadata(), + statsMessage.getData(), nullptr, nullptr); + } + } + + if (sessionAccessor.current()->first != currentSessionId) + sessionFound = sessionAccessor.fastRemove(); + else + sessionFound = sessionAccessor.getNext(); + } +} + Profiler::Request* Profiler::getRequest(jrd_req* request) { const auto profileSession = sessions.get(currentSessionId); diff --git a/src/jrd/Profiler.h b/src/jrd/Profiler.h index d7b23497c1..c46e21acf7 100644 --- a/src/jrd/Profiler.h +++ b/src/jrd/Profiler.h @@ -38,37 +38,8 @@ class thread_db; class Profiler; -class ProfileSnapshotData : public SnapshotData -{ -public: - ProfileSnapshotData(thread_db* tdbb); - -public: - void update(thread_db* tdbb, Profiler* profiler); - void reset(thread_db* tdbb); - -private: - void allocBuffers(thread_db* tdbb); - -private: - MemoryPool& pool; -}; - - -class ProfileTableScan : public VirtualTableScan -{ -public: - using VirtualTableScan::VirtualTableScan; - -protected: - const Format* getFormat(thread_db* tdbb, jrd_rel* relation) const override; - bool retrieveRecord(thread_db* tdbb, jrd_rel* relation, FB_UINT64 position, Record* record) const override; -}; - - class Profiler { - friend class ProfileSnapshotData; friend class ProfileTableScan; friend class ProfilerPackage; @@ -202,6 +173,8 @@ private: ULONG((lineColumn >> 32) & 0xFFFFFFFF), ULONG(lineColumn & 0xFFFFFFFF)); } + void updateSnapshot(thread_db* tdbb); + Request* getRequest(jrd_req* request); private: @@ -209,8 +182,6 @@ private: bool activeSession = false; bool paused = false; Firebird::RightPooledMap sessions; - ProfileSnapshotData snapshotData; - SnapshotCounters previousSnapshotCounters; }; diff --git a/src/jrd/constants.h b/src/jrd/constants.h index 97e6900214..b518f8bf73 100644 --- a/src/jrd/constants.h +++ b/src/jrd/constants.h @@ -142,6 +142,7 @@ const int GEN_SECCLASS_PREFIX_LEN = 4; const char* const PROCEDURES_GENERATOR = "RDB$PROCEDURES"; const char* const FUNCTIONS_GENERATOR = "RDB$FUNCTIONS"; +const char* const PROFILE_SESSION_GENERATOR = "RDB$PROFILE_SESSION_ID"; // Automatically created check constraints for unnamed PRIMARY and UNIQUE declarations. const char* const IMPLICIT_INTEGRITY_PREFIX = "INTEG_"; diff --git a/src/jrd/idx.h b/src/jrd/idx.h index 1ab8fb4e17..a5205e0631 100644 --- a/src/jrd/idx.h +++ b/src/jrd/idx.h @@ -30,7 +30,7 @@ /* Indices to be created */ /* Maxinum number of segments in any existing system index */ -const int INI_IDX_MAX_SEGMENTS = 3; +const int INI_IDX_MAX_SEGMENTS = 4; struct ini_idx_t { @@ -304,6 +304,31 @@ static const struct ini_idx_t indices[] = SEGMENT(f_pubtab_tab_name, idx_string), // table name SEGMENT(f_pubtab_pub_name, idx_string) // publication name }}, + // define index RDB$INDEX_57 for RDB$PROFILE_SESSIONS unique RDB$PROFILE_SESSION_ID; + INDEX(57, rel_prof_sessions, idx_unique, 1) + SEGMENT(f_prof_ses_id, idx_numeric) // profile session id + }}, + // define index RDB$INDEX_58 for RDB$PROFILE_REQUESTS unique RDB$PROFILE_SESSION_ID, RDB$PROFILE_REQUEST_ID; + INDEX(58, rel_prof_requests, idx_unique, 2) + SEGMENT(f_prof_req_ses_id, idx_numeric), // profile session id + SEGMENT(f_prof_req_req_id, idx_numeric) // profile request id + }}, + // define index RDB$INDEX_59 for RDB$PROFILE_STATS unique + // RDB$PROFILE_SESSION_ID, RDB$PROFILE_REQUEST_ID, RDB$LINE, RDB$COLUMN; + INDEX(59, rel_prof_stats, idx_unique, 4) + SEGMENT(f_prof_stats_ses_id, idx_numeric), // profile session id + SEGMENT(f_prof_stats_req_id, idx_numeric), // profile request id + SEGMENT(f_prof_stats_line, idx_numeric), // line number + SEGMENT(f_prof_stats_column, idx_numeric) // column number + }}, + // define index RDB$INDEX_60 for RDB$PROFILE_RECORD_SOURCE_STATS unique + // RDB$PROFILE_SESSION_ID, RDB$PROFILE_REQUEST_ID, RDB$CURSOR_ID, RDB$RECORD_SOURCE_ID; + INDEX(60, rel_prof_recsrc_stats, idx_unique, 4) + SEGMENT(f_prof_recsrc_stats_ses_id, idx_numeric), // profile session id + SEGMENT(f_prof_recsrc_stats_req_id, idx_numeric), // profile request id + SEGMENT(f_prof_recsrc_stats_cursor_id, idx_numeric), // cursor id + SEGMENT(f_prof_recsrc_stats_recsrc_id, idx_numeric) // record source id + }}, }; #define SYSTEM_INDEX_COUNT FB_NELEM(indices) diff --git a/src/jrd/opt.cpp b/src/jrd/opt.cpp index 92c6a935e4..fbc127009d 100644 --- a/src/jrd/opt.cpp +++ b/src/jrd/opt.cpp @@ -2298,13 +2298,6 @@ static RecordSource* gen_retrieval(thread_db* tdbb, rsb = FB_NEW_POOL(*tdbb->getDefaultPool()) TimeZonesTableScan(csb, alias, stream, relation); break; - case rel_prof_sessions: - case rel_prof_requests: - case rel_prof_stats: - case rel_prof_recsrc_stats: - rsb = FB_NEW_POOL(*tdbb->getDefaultPool()) ProfileTableScan(csb, alias, stream, relation); - break; - default: rsb = FB_NEW_POOL(*tdbb->getDefaultPool()) MonitoringTableScan(csb, alias, stream, relation); break; diff --git a/src/jrd/relations.h b/src/jrd/relations.h index e9f948de28..0d5b425e9b 100644 --- a/src/jrd/relations.h +++ b/src/jrd/relations.h @@ -728,15 +728,17 @@ END_RELATION //// FIXME: Nullable fields. // Relation 53 (RDB$PROFILE_SESSIONS) -RELATION(nam_prof_ses, rel_prof_sessions, ODS_13_0, rel_virtual) +RELATION(nam_prof_ses, rel_prof_sessions, ODS_13_0, rel_persistent) FIELD(f_prof_ses_id, nam_prof_ses_id, fld_prof_ses_id, 0, ODS_13_0) + FIELD(f_prof_ses_att_id, nam_att_id, fld_att_id, 0, ODS_13_0) + FIELD(f_prof_ses_user, nam_user, fld_user, 0, ODS_13_0) FIELD(f_prof_ses_desc, nam_description, fld_short_description, 0, ODS_13_0) FIELD(f_prof_ses_start_timestamp, nam_start_timestamp, fld_timestamp_tz, 0, ODS_13_0) FIELD(f_prof_ses_finish_timestamp, nam_finish_timestamp, fld_timestamp_tz, 0, ODS_13_0) END_RELATION // Relation 54 (RDB$PROFILE_REQUESTS) -RELATION(nam_prof_requests, rel_prof_requests, ODS_13_0, rel_virtual) +RELATION(nam_prof_requests, rel_prof_requests, ODS_13_0, rel_persistent) FIELD(f_prof_req_ses_id, nam_prof_ses_id, fld_prof_ses_id, 0, ODS_13_0) FIELD(f_prof_req_req_id, nam_prof_req_id, fld_stmt_id, 0, ODS_13_0) FIELD(f_prof_req_time, nam_time, fld_timestamp_tz, 0, ODS_13_0) @@ -747,7 +749,7 @@ RELATION(nam_prof_requests, rel_prof_requests, ODS_13_0, rel_virtual) END_RELATION // Relation 55 (RDB$PROFILE_STATS) -RELATION(nam_prof_stats, rel_prof_stats, ODS_13_0, rel_virtual) +RELATION(nam_prof_stats, rel_prof_stats, ODS_13_0, rel_persistent) FIELD(f_prof_stats_ses_id, nam_prof_ses_id, fld_prof_ses_id, 0, ODS_13_0) FIELD(f_prof_stats_req_id, nam_prof_req_id, fld_stmt_id, 0, ODS_13_0) FIELD(f_prof_stats_line, nam_line, fld_src_info, 0, ODS_13_0) @@ -759,7 +761,7 @@ RELATION(nam_prof_stats, rel_prof_stats, ODS_13_0, rel_virtual) END_RELATION // Relation 56 (RDB$PROFILE_RECORD_SOURCE_STATS) -RELATION(nam_prof_recsrc_stats, rel_prof_recsrc_stats, ODS_13_0, rel_virtual) +RELATION(nam_prof_recsrc_stats, rel_prof_recsrc_stats, ODS_13_0, rel_persistent) FIELD(f_prof_recsrc_stats_ses_id, nam_prof_ses_id, fld_prof_ses_id, 0, ODS_13_0) FIELD(f_prof_recsrc_stats_req_id, nam_prof_req_id, fld_stmt_id, 0, ODS_13_0) FIELD(f_prof_recsrc_stats_cursor_id, nam_cursor_id, fld_cursor_id, 0, ODS_13_0) diff --git a/src/jrd/trig.h b/src/jrd/trig.h index 569a0e51c0..9f9285a62a 100644 --- a/src/jrd/trig.h +++ b/src/jrd/trig.h @@ -81,6 +81,7 @@ static const Jrd::gen generators[] = { "RDB$BACKUP_HISTORY", 9, "Nbackup technology" }, { FUNCTIONS_GENERATOR, 10, "Function ID" }, { "RDB$GENERATOR_NAME", 11, "Implicit generator name" }, + { PROFILE_SESSION_GENERATOR, 12, "Profile Session ID" }, { nullptr, 0, nullptr } };