mirror of
https://github.com/FirebirdSQL/firebird.git
synced 2025-01-30 19:23:03 +01:00
4759973045
Full rebuild required - CMP_compile2 prototype changed
545 lines
11 KiB
C++
545 lines
11 KiB
C++
/*
|
|
* PROGRAM: Firebird Trace Services
|
|
* MODULE: TraceObjects.h
|
|
* DESCRIPTION: Trace API manager support
|
|
*
|
|
* The contents of this file are subject to the Initial
|
|
* Developer's Public License Version 1.0 (the "License");
|
|
* you may not use this file except in compliance with the
|
|
* License. You may obtain a copy of the License at
|
|
* http://www.ibphoenix.com/main.nfs?a=ibphoenix&page=ibp_idpl.
|
|
*
|
|
* Software distributed under the License is distributed AS IS,
|
|
* WITHOUT WARRANTY OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing rights
|
|
* and limitations under the License.
|
|
*
|
|
* The Original Code was created by Khorsun Vladyslav
|
|
* for the Firebird Open Source RDBMS project.
|
|
*
|
|
* Copyright (c) 2009 Khorsun Vladyslav <hvlad@users.sourceforge.net>
|
|
* and all contributors signed below.
|
|
*
|
|
* All Rights Reserved.
|
|
* Contributor(s): ______________________________________.
|
|
*
|
|
*/
|
|
|
|
#include "firebird.h"
|
|
#include "../../jrd/common.h"
|
|
#include "../../common/classes/auto.h"
|
|
#include "../../common/utils_proto.h"
|
|
#include "../../jrd/trace/TraceManager.h"
|
|
#include "../../jrd/trace/TraceLog.h"
|
|
#include "../../jrd/trace/TraceObjects.h"
|
|
#include "../../jrd/isc_proto.h"
|
|
#include "../../jrd/isc_s_proto.h"
|
|
#include "../../jrd/jrd.h"
|
|
#include "../../jrd/jrd_pwd.h"
|
|
#include "../../jrd/tra.h"
|
|
#include "../../jrd/evl_proto.h"
|
|
#include "../../jrd/mov_proto.h"
|
|
#include "../../jrd/pag_proto.h"
|
|
#include "../../jrd/os/path_utils.h"
|
|
#include "../../jrd/os/config_root.h"
|
|
#include "../../dsql/dsql_proto.h"
|
|
#include "../../gpre/prett_proto.h"
|
|
|
|
#ifdef WIN_NT
|
|
#include <process.h>
|
|
#endif
|
|
#ifdef HAVE_UNISTD_H
|
|
#include <unistd.h>
|
|
#endif
|
|
|
|
using namespace Firebird;
|
|
|
|
namespace Jrd {
|
|
|
|
/// TraceConnectionImpl
|
|
|
|
int TraceConnectionImpl::getConnectionID()
|
|
{
|
|
//return m_att->att_attachment_id;
|
|
return PAG_attachment_id(JRD_get_thread_data());
|
|
}
|
|
|
|
int TraceConnectionImpl::getProcessID()
|
|
{
|
|
return getpid();
|
|
}
|
|
|
|
const char* TraceConnectionImpl::getDatabaseName()
|
|
{
|
|
return m_att->att_filename.c_str();
|
|
}
|
|
|
|
const char* TraceConnectionImpl::getUserName()
|
|
{
|
|
const UserId* user = m_att->att_user;
|
|
return user ? user->usr_user_name.c_str() : NULL;
|
|
}
|
|
|
|
const char* TraceConnectionImpl::getRoleName()
|
|
{
|
|
const UserId* user = m_att->att_user;
|
|
return user ? user->usr_sql_role_name.c_str() : NULL;
|
|
}
|
|
|
|
const char* TraceConnectionImpl::getRemoteProtocol()
|
|
{
|
|
return m_att->att_network_protocol.c_str();
|
|
}
|
|
|
|
const char* TraceConnectionImpl::getRemoteAddress()
|
|
{
|
|
return m_att->att_remote_address.c_str();
|
|
}
|
|
|
|
int TraceConnectionImpl::getRemoteProcessID()
|
|
{
|
|
return m_att->att_remote_pid;
|
|
}
|
|
|
|
const char* TraceConnectionImpl::getRemoteProcessName()
|
|
{
|
|
return m_att->att_remote_process.c_str();
|
|
}
|
|
|
|
|
|
/// TraceTransactionImpl
|
|
|
|
int TraceTransactionImpl::getTransactionID()
|
|
{
|
|
return m_tran->tra_number;
|
|
}
|
|
|
|
bool TraceTransactionImpl::getReadOnly()
|
|
{
|
|
return (m_tran->tra_flags & TRA_readonly);
|
|
}
|
|
|
|
int TraceTransactionImpl::getWait()
|
|
{
|
|
return -m_tran->getLockWait();
|
|
}
|
|
|
|
ntrace_tra_isolation_t TraceTransactionImpl::getIsolation()
|
|
{
|
|
switch (m_tran->tra_flags & (TRA_read_committed | TRA_rec_version | TRA_degree3))
|
|
{
|
|
case TRA_degree3:
|
|
return tra_iso_consistency;
|
|
|
|
case TRA_read_committed:
|
|
return tra_iso_read_committed_norecver;
|
|
|
|
case TRA_read_committed | TRA_rec_version:
|
|
return tra_iso_read_committed_recver;
|
|
|
|
case 0:
|
|
return tra_iso_concurrency;
|
|
|
|
default:
|
|
fb_assert(false);
|
|
return tra_iso_concurrency;
|
|
}
|
|
}
|
|
|
|
|
|
/// TraceDYNRequestImpl
|
|
|
|
const char* TraceDYNRequestImpl::getText()
|
|
{
|
|
if (m_text.empty() && m_length) {
|
|
PRETTY_print_dyn((UCHAR*) m_ddl, print_dyn, this, 0);
|
|
}
|
|
return m_text.c_str();
|
|
}
|
|
|
|
void TraceDYNRequestImpl::print_dyn(void* arg, SSHORT offset, const char* line)
|
|
{
|
|
TraceDYNRequestImpl *dyn = (TraceDYNRequestImpl*) arg;
|
|
|
|
string temp;
|
|
temp.printf("%4d %s\n", offset, line);
|
|
dyn->m_text.append(temp);
|
|
}
|
|
|
|
|
|
/// BLRPrinter
|
|
|
|
const char* BLRPrinter::getText()
|
|
{
|
|
if (m_text.empty() && getDataLength())
|
|
isc_print_blr2(getData(), (USHORT) getDataLength(), print_blr, this, 0);
|
|
return m_text.c_str();
|
|
}
|
|
|
|
void BLRPrinter::print_blr(void* arg, SSHORT offset, const char* line)
|
|
{
|
|
BLRPrinter* blr = (BLRPrinter*) arg;
|
|
|
|
string temp;
|
|
temp.printf("%4d %s\n", offset, line);
|
|
blr->m_text.append(temp);
|
|
}
|
|
|
|
|
|
/// TraceSQLStatementImpl
|
|
|
|
TraceSQLStatementImpl::~TraceSQLStatementImpl()
|
|
{
|
|
if (m_plan)
|
|
gds__free(m_plan);
|
|
}
|
|
|
|
int TraceSQLStatementImpl::getStmtID()
|
|
{
|
|
if (m_stmt->req_request)
|
|
return m_stmt->req_request->req_id;
|
|
|
|
return 0;
|
|
}
|
|
|
|
const char* TraceSQLStatementImpl::getText()
|
|
{
|
|
return m_stmt->req_sql_text->c_str();
|
|
}
|
|
|
|
const char* TraceSQLStatementImpl::getPlan()
|
|
{
|
|
if (!m_plan && m_stmt->req_request)
|
|
{
|
|
char buff;
|
|
m_plan = &buff;
|
|
|
|
const size_t len = DSQL_get_plan_info(JRD_get_thread_data(),
|
|
m_stmt, sizeof(buff), &m_plan);
|
|
|
|
if (len)
|
|
m_plan[len] = 0;
|
|
else
|
|
m_plan = NULL;
|
|
}
|
|
|
|
return m_plan;
|
|
}
|
|
|
|
PerformanceInfo* TraceSQLStatementImpl::getPerf()
|
|
{
|
|
return m_perf;
|
|
}
|
|
|
|
TraceParams* TraceSQLStatementImpl::getInputs()
|
|
{
|
|
return &m_inputs;
|
|
}
|
|
|
|
|
|
/// TraceSQLStatementImpl::DSQLParamsImpl
|
|
|
|
void TraceSQLStatementImpl::DSQLParamsImpl::fillParams()
|
|
{
|
|
if (m_descs.getCount() || !m_params)
|
|
return;
|
|
|
|
USHORT first_index = 0;
|
|
for (const dsql_par* parameter = m_params; parameter; parameter = parameter->par_next)
|
|
{
|
|
if (parameter->par_index)
|
|
{
|
|
if (!first_index)
|
|
first_index = parameter->par_index;
|
|
|
|
// Use descriptor for nulls signaling
|
|
USHORT null_flag = 0;
|
|
if (parameter->par_null &&
|
|
*((SSHORT*) parameter->par_null->par_desc.dsc_address))
|
|
{
|
|
null_flag = DSC_null;
|
|
}
|
|
|
|
if (first_index > parameter->par_index)
|
|
{
|
|
m_descs.insert(0, parameter->par_desc);
|
|
m_descs.front().dsc_flags |= null_flag;
|
|
}
|
|
else
|
|
{
|
|
m_descs.add(parameter->par_desc);
|
|
m_descs.back().dsc_flags |= null_flag;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
size_t TraceSQLStatementImpl::DSQLParamsImpl::getCount()
|
|
{
|
|
fillParams();
|
|
return m_descs.getCount();
|
|
}
|
|
|
|
const dsc* TraceSQLStatementImpl::DSQLParamsImpl::getParam(size_t idx)
|
|
{
|
|
fillParams();
|
|
|
|
if (idx >= 0 && idx < m_descs.getCount())
|
|
return &m_descs[idx];
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
|
|
/// TraceProcedureImpl::JrdParamsImpl
|
|
|
|
size_t TraceProcedureImpl::JrdParamsImpl::getCount()
|
|
{
|
|
fillParams();
|
|
return m_descs.getCount();
|
|
}
|
|
|
|
const dsc* TraceProcedureImpl::JrdParamsImpl::getParam(size_t idx)
|
|
{
|
|
fillParams();
|
|
|
|
if (idx >= 0 && idx < m_descs.getCount())
|
|
return &m_descs[idx];
|
|
|
|
return NULL;
|
|
}
|
|
|
|
void TraceProcedureImpl::JrdParamsImpl::fillParams()
|
|
{
|
|
if (m_descs.getCount() || !m_params)
|
|
return;
|
|
|
|
thread_db* tdbb = JRD_get_thread_data();
|
|
|
|
const jrd_nod* const* ptr = m_params->nod_arg;
|
|
const jrd_nod* const* end = ptr + m_params->nod_count;
|
|
for (; ptr < end; ptr++)
|
|
{
|
|
dsc* from_desc = NULL;
|
|
dsc desc;
|
|
|
|
const jrd_nod* const prm = (*ptr)->nod_arg[e_asgn_to];
|
|
switch (prm->nod_type)
|
|
{
|
|
case nod_argument:
|
|
{
|
|
//const impure_value* impure = (impure_value*) ((SCHAR*) m_request + prm->nod_impure);
|
|
const jrd_nod* message = prm->nod_arg[e_arg_message];
|
|
const Format* format = (Format*) message->nod_arg[e_msg_format];
|
|
const int arg_number = (int) (IPTR) prm->nod_arg[e_arg_number];
|
|
|
|
desc = format->fmt_desc[arg_number];
|
|
from_desc = &desc;
|
|
from_desc->dsc_address = (UCHAR *) m_request + message->nod_impure + (IPTR) desc.dsc_address;
|
|
|
|
// handle null flag if present
|
|
if (prm->nod_arg[e_arg_flag])
|
|
{
|
|
const dsc* flag = EVL_expr(tdbb, prm->nod_arg[e_arg_flag]);
|
|
if (MOV_get_long(flag, 0)) {
|
|
from_desc->dsc_flags |= DSC_null;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
case nod_variable:
|
|
{
|
|
impure_value* impure = (impure_value*) ((SCHAR *) m_request + prm->nod_impure);
|
|
from_desc = &impure->vlu_desc;
|
|
break;
|
|
}
|
|
|
|
case nod_null:
|
|
desc = ((Literal*) prm)->lit_desc;
|
|
from_desc = &desc;
|
|
from_desc->dsc_flags |= DSC_null;
|
|
break;
|
|
|
|
case nod_literal:
|
|
from_desc = &((Literal*) prm)->lit_desc;
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (from_desc)
|
|
m_descs.add(*from_desc);
|
|
}
|
|
}
|
|
|
|
|
|
/// TraceTriggerImpl
|
|
|
|
const char* TraceTriggerImpl::getTriggerName()
|
|
{
|
|
return m_trig->req_trg_name.c_str();
|
|
}
|
|
|
|
const char* TraceTriggerImpl::getRelationName()
|
|
{
|
|
const jrd_rel* rel = m_trig->req_rpb->rpb_relation;
|
|
return rel ? rel->rel_name.c_str() : NULL;
|
|
}
|
|
|
|
|
|
/// TraceLogWriterImpl
|
|
|
|
class TraceLogWriterImpl : public TraceLogWriter
|
|
{
|
|
public:
|
|
TraceLogWriterImpl(MemoryPool& pool, const TraceSession& session) :
|
|
m_log(pool, session.ses_logfile, false),
|
|
m_sesId(session.ses_id)
|
|
{
|
|
m_maxSize = Config::getMaxUserTraceLogSize();
|
|
}
|
|
|
|
virtual size_t write(const void* buf, size_t size);
|
|
|
|
virtual void release()
|
|
{
|
|
delete this;
|
|
}
|
|
|
|
private:
|
|
TraceLog m_log;
|
|
ULONG m_sesId;
|
|
size_t m_maxSize;
|
|
};
|
|
|
|
size_t TraceLogWriterImpl::write(const void* buf, size_t size)
|
|
{
|
|
// comparison is in MB
|
|
if (m_log.getApproxLogSize() <= m_maxSize)
|
|
return m_log.write(buf, size);
|
|
|
|
ConfigStorage* storage = TraceManager::getStorage();
|
|
StorageGuard guard(storage);
|
|
|
|
TraceSession session(*getDefaultMemoryPool());
|
|
storage->restart();
|
|
while (storage->getNextSession(session))
|
|
{
|
|
if (session.ses_id == m_sesId)
|
|
{
|
|
if (!(session.ses_flags & trs_log_full))
|
|
{
|
|
// suspend session
|
|
session.ses_flags |= trs_log_full;
|
|
storage->updateSession(session);
|
|
|
|
string s;
|
|
s.printf("\n--- Session %d is suspended as its log is full ---\n", m_sesId);
|
|
m_log.write(s.c_str(), s.length());
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
// report successful write
|
|
return size;
|
|
}
|
|
|
|
|
|
/// TraceInitInfoImpl
|
|
|
|
const char* TraceInitInfoImpl::getFirebirdRootDirectory()
|
|
{
|
|
return Config::getRootDirectory();
|
|
}
|
|
|
|
TraceLogWriter* TraceInitInfoImpl::getLogWriter()
|
|
{
|
|
if (!m_logWriter && !m_session.ses_logfile.empty())
|
|
{
|
|
MemoryPool &pool = *getDefaultMemoryPool();
|
|
m_logWriter = FB_NEW(pool) TraceLogWriterImpl(pool, m_session);
|
|
}
|
|
return m_logWriter;
|
|
}
|
|
|
|
|
|
/// TraceServiceImpl
|
|
|
|
const ntrace_service_t TraceServiceImpl::getServiceID()
|
|
{
|
|
return (ntrace_service_t) m_svc;
|
|
}
|
|
|
|
const char* TraceServiceImpl::getServiceMgr()
|
|
{
|
|
return m_svc->getServiceMgr();
|
|
}
|
|
|
|
const char* TraceServiceImpl::getServiceName()
|
|
{
|
|
return m_svc->getServiceName();
|
|
}
|
|
|
|
int TraceServiceImpl::getProcessID()
|
|
{
|
|
return getpid();
|
|
}
|
|
|
|
const char* TraceServiceImpl::getUserName()
|
|
{
|
|
return m_svc->getUserName().c_str();
|
|
}
|
|
|
|
const char* TraceServiceImpl::getRoleName()
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
const char* TraceServiceImpl::getRemoteProtocol()
|
|
{
|
|
return m_svc->getNetworkProtocol().c_str();
|
|
}
|
|
|
|
const char* TraceServiceImpl::getRemoteAddress()
|
|
{
|
|
return m_svc->getRemoteAddress().c_str();
|
|
}
|
|
|
|
int TraceServiceImpl::getRemoteProcessID()
|
|
{
|
|
return m_svc->getRemotePID();
|
|
}
|
|
|
|
const char* TraceServiceImpl::getRemoteProcessName()
|
|
{
|
|
return m_svc->getRemoteProcess().c_str();
|
|
}
|
|
|
|
|
|
/// TraceRuntimeStats
|
|
|
|
TraceRuntimeStats::TraceRuntimeStats(Database* dbb, RuntimeStatistics* baseline, RuntimeStatistics* stats,
|
|
SINT64 clock, SINT64 records_fetched)
|
|
{
|
|
m_info.pin_time = clock * 1000 / fb_utils::query_performance_frequency();
|
|
m_info.pin_records_fetched = records_fetched;
|
|
|
|
if (baseline)
|
|
baseline->computeDifference(dbb, *stats, m_info, m_counts);
|
|
else
|
|
{
|
|
// Report all zero counts for the moment.
|
|
memset(&m_info, 0, sizeof(m_info));
|
|
m_info.pin_counters = m_dummy_counts;
|
|
}
|
|
}
|
|
|
|
SINT64 TraceRuntimeStats::m_dummy_counts[DBB_max_dbb_count] = {0};
|
|
|
|
} // namespace Jrd
|