/* * PROGRAM: JRD Access Method * MODULE: TraceManager.cpp * DESCRIPTION: Trace API manager * * 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 Nickolay Samofatov * for the Firebird Open Source RDBMS project. * * Copyright (c) 2004 Nickolay Samofatov * and all contributors signed below. * * All Rights Reserved. * Contributor(s): ______________________________________. * * 2008 Khorsun Vladyslav */ #include "firebird.h" #include "../../jrd/common.h" #include "../../jrd/trace/TraceManager.h" #include "../../jrd/trace/TraceObjects.h" #include "../../jrd/plugin_manager.h" #include "../../jrd/os/path_utils.h" #ifdef WIN_NT #include #endif using namespace Firebird; namespace { static const char* NTRACE_PREFIX = "fbtrace"; } namespace Jrd { GlobalPtr > TraceManager::modules; GlobalPtr TraceManager::init_modules_mtx; bool TraceManager::init_modules = false; StorageInstance TraceManager::storageInstance; bool TraceManager::check_result(const TracePlugin* plugin, const char* module, const char* function, bool result) { if (result) return true; if (!plugin) { gds__log("Trace plugin %s returned error on call %s, " "did not create plugin and provided no additional details on reasons of failure", module, function); return false; } const char* errorStr = plugin->tpl_get_error(plugin); if (!errorStr) { gds__log("Trace plugin %s returned error on call %s, " "but provided no additional details on reasons of failure", module, function); return false; } gds__log("Trace plugin %s returned error on call %s. Error details: %s", module, function, errorStr); return false; } TraceManager::TraceManager(Attachment* _att) : attachment(_att), service(NULL), filename(NULL), trace_sessions(*_att->att_pool) { change_number = 0; storage = storageInstance.getStorage(); load_modules(); } TraceManager::TraceManager(Service* _svc) : attachment(NULL), service(_svc), filename(NULL), trace_sessions(_svc->getPool()) { change_number = 0; storage = storageInstance.getStorage(); load_modules(); } TraceManager::TraceManager(const char* _filename) : attachment(NULL), service(NULL), filename(_filename), trace_sessions(*getDefaultMemoryPool()) { change_number = 0; storage = storageInstance.getStorage(); load_modules(); } TraceManager::~TraceManager() { // Destroy all plugins for (SessionInfo* info = trace_sessions.begin(); info < trace_sessions.end(); info++) { check_result(NULL, info->module_info->module, "tpl_shutdown", info->plugin->tpl_shutdown(info->plugin)); } } void TraceManager::load_modules() { // Initialize all trace needs to false memset(&trace_needs, 0, sizeof(trace_needs)); if (init_modules) return; MutexLockGuard guard(init_modules_mtx); if (init_modules) return; init_modules = true; for (PluginManager::iterator i = PluginManager::enginePluginManager().begin(); *i; ++i) { PluginManager::Plugin p = *i; const PathName* name = p.name(); if (!name) continue; PathName pathComponent, modName; PathUtils::splitLastComponent(pathComponent, modName, *name); if (modName.substr(0, strlen(NTRACE_PREFIX)) != NTRACE_PREFIX) continue; ntrace_attach_t ntrace_attach_func = (ntrace_attach_t) ((*i).lookupSymbol(NTRACE_ATTACH)); if (!ntrace_attach_func) continue; ModuleInfo info; info.ntrace_attach = ntrace_attach_func; strncpy(info.module, modName.c_str(), sizeof(info.module)); info.module[sizeof(info.module)-1] = 0; modules->add(info); } } void TraceManager::update_sessions() { SortedArray liveSessions(*getDefaultMemoryPool()); { // scope StorageGuard guard(storage); storage->restart(); TraceSession session(*getDefaultMemoryPool()); while (storage->getNextSession(session)) { if ((session.ses_flags & trs_active) && !(session.ses_flags & trs_log_full)) { update_session(session); liveSessions.add(session.ses_id); } } change_number = storage->getChangeNumber(); } // remove sessions not present in storage size_t i = 0; while (i < trace_sessions.getCount()) { size_t pos; if (liveSessions.find(trace_sessions[i].ses_id, pos)) { i++; } else { const TracePlugin* p = trace_sessions[i].plugin; ModuleInfo* info = trace_sessions[i].module_info; check_result(p, info->module, "tpl_shutdown", p->tpl_shutdown(p)); trace_sessions.remove(i); } } } void TraceManager::update_session(const TraceSession& session) { // if this session is already known, nothing to do size_t pos; if (trace_sessions.find(session.ses_id, pos)) { return; } // if this session is not from administrator, it may trace connections // only created by the same user if (!(session.ses_flags & trs_admin)) { if (attachment) { if (!attachment->att_user || attachment->att_user->usr_user_name != session.ses_user) return; } else if (service) { if (session.ses_user != service->getUserName()) return; } else { // failed attachment attempts traced by admin trace only return; } } ModuleInfo* info = modules->begin(); for (; info < modules->end(); info++) { TraceInitInfoImpl attachInfo(session, attachment, filename); const TracePlugin* plugin = NULL; if (!check_result(plugin, info->module, NTRACE_ATTACH, info->ntrace_attach(&attachInfo, &plugin))) { // This was a skeletal plugin to return an error if (plugin && plugin->tpl_shutdown) plugin->tpl_shutdown(plugin); plugin = NULL; } if (plugin && plugin->tpl_version != NTRACE_VERSION) { gds__log("Module \"%s\" created trace plugin version %d. Supported version %d", info->module, plugin->tpl_version, NTRACE_VERSION); // plugin->tpl_shutdown(plugin); plugin = NULL; modules->remove(info); info--; } if (plugin) { SessionInfo sesInfo; sesInfo.plugin = plugin; sesInfo.module_info = info; sesInfo.ses_id = session.ses_id; trace_sessions.add(sesInfo); if (plugin->tpl_event_attach) trace_needs.event_attach = true; if (plugin->tpl_event_detach) trace_needs.event_detach = true; if (plugin->tpl_event_transaction_start) trace_needs.event_transaction_start = true; if (plugin->tpl_event_transaction_end) trace_needs.event_transaction_end = true; if (plugin->tpl_event_set_context) trace_needs.event_set_context = true; if (plugin->tpl_event_proc_execute) trace_needs.event_proc_execute = true; if (plugin->tpl_event_dsql_prepare) trace_needs.event_dsql_prepare = true; if (plugin->tpl_event_dsql_free) trace_needs.event_dsql_free = true; if (plugin->tpl_event_dsql_execute) trace_needs.event_dsql_execute = true; if (plugin->tpl_event_blr_compile) trace_needs.event_blr_compile = true; if (plugin->tpl_event_blr_execute) trace_needs.event_blr_execute = true; if (plugin->tpl_event_dyn_execute) trace_needs.event_dyn_execute = true; if (plugin->tpl_event_service_attach) trace_needs.event_service_attach = true; if (plugin->tpl_event_service_start) trace_needs.event_service_start = true; if (plugin->tpl_event_service_query) trace_needs.event_service_query = true; if (plugin->tpl_event_service_detach) trace_needs.event_service_detach = true; if (plugin->tpl_event_trigger_execute) trace_needs.event_trigger_execute = true; } } } bool TraceManager::need_dsql_prepare(Attachment* att) { return att->att_trace_manager->needs().event_dsql_prepare; } bool TraceManager::need_dsql_free(Attachment* att) { return att->att_trace_manager->needs().event_dsql_free; } bool TraceManager::need_dsql_execute(Attachment* att) { return att->att_trace_manager->needs().event_dsql_execute; } void TraceManager::event_dsql_prepare(Attachment* att, jrd_tra* transaction, TraceSQLStatement* statement, ntrace_counter_t time_millis, ntrace_result_t req_result) { TraceConnectionImpl conn(att); TraceTransactionImpl tran(transaction); att->att_trace_manager->event_dsql_prepare( &conn, transaction ? &tran : NULL, statement, time_millis, req_result); } void TraceManager::event_dsql_free(Attachment* att, TraceSQLStatement* statement, unsigned short option) { TraceConnectionImpl conn(att); att->att_trace_manager->event_dsql_free(&conn, statement, option); } void TraceManager::event_dsql_execute(Attachment* att, jrd_tra* transaction, TraceSQLStatement* statement, bool started, ntrace_result_t req_result) { TraceConnectionImpl conn(att); TraceTransactionImpl tran(transaction); att->att_trace_manager->event_dsql_execute(&conn, &tran, statement, started, req_result); } #define EXECUTE_HOOKS(METHOD, PARAMS) \ size_t i = 0; \ while (i < trace_sessions.getCount()) \ { \ SessionInfo* plug_info = &trace_sessions[i]; \ if (!plug_info->plugin->METHOD || \ check_result(plug_info->plugin, plug_info->module_info->module, #METHOD, \ plug_info->plugin->METHOD PARAMS)) \ { \ i++; /* Move to next plugin */ \ } \ else { \ trace_sessions.remove(i); /* Remove broken plugin from the list */ \ } \ } void TraceManager::event_attach(TraceConnection* connection, bool create_db, ntrace_result_t att_result) { EXECUTE_HOOKS(tpl_event_attach, (plug_info->plugin, connection, create_db, att_result)); } void TraceManager::event_detach(TraceConnection* connection, bool drop_db) { EXECUTE_HOOKS(tpl_event_detach, (plug_info->plugin, connection, drop_db)); } void TraceManager::event_transaction_start(TraceConnection* connection, TraceTransaction* transaction, size_t tpb_length, const ntrace_byte_t* tpb, ntrace_result_t tra_result) { EXECUTE_HOOKS(tpl_event_transaction_start, (plug_info->plugin, connection, transaction, tpb_length, tpb, tra_result)); } void TraceManager::event_transaction_end(TraceConnection* connection, TraceTransaction* transaction, bool commit, bool retain_context, ntrace_result_t tra_result) { EXECUTE_HOOKS(tpl_event_transaction_end, (plug_info->plugin, connection, transaction, commit, retain_context, tra_result)); } void TraceManager::event_set_context(TraceConnection* connection, TraceTransaction* transaction, TraceContextVariable* variable) { EXECUTE_HOOKS(tpl_event_set_context, (plug_info->plugin, connection, transaction, variable)); } void TraceManager::event_proc_execute(TraceConnection* connection, TraceTransaction* transaction, TraceProcedure* procedure, bool started, ntrace_result_t proc_result) { EXECUTE_HOOKS(tpl_event_proc_execute, (plug_info->plugin, connection, transaction, procedure, started, proc_result)); } void TraceManager::event_trigger_execute(TraceConnection* connection, TraceTransaction* transaction, TraceTrigger* trigger, bool started, ntrace_result_t trig_result) { EXECUTE_HOOKS(tpl_event_trigger_execute, (plug_info->plugin, connection, transaction, trigger, started, trig_result)); } void TraceManager::event_dsql_prepare(TraceConnection* connection, TraceTransaction* transaction, TraceSQLStatement* statement, ntrace_counter_t time_millis, ntrace_result_t req_result) { EXECUTE_HOOKS(tpl_event_dsql_prepare, (plug_info->plugin, connection, transaction, statement, time_millis, req_result)); } void TraceManager::event_dsql_free(TraceConnection* connection, TraceSQLStatement* statement, unsigned short option) { EXECUTE_HOOKS(tpl_event_dsql_free, (plug_info->plugin, connection, statement, option)); } void TraceManager::event_dsql_execute(TraceConnection* connection, TraceTransaction* transaction, TraceSQLStatement* statement, bool started, ntrace_result_t req_result) { EXECUTE_HOOKS(tpl_event_dsql_execute, (plug_info->plugin, connection, transaction, statement, started, req_result)); } void TraceManager::event_blr_compile(TraceConnection* connection, TraceTransaction* transaction, TraceBLRStatement* statement, ntrace_counter_t time_millis, ntrace_result_t req_result) { EXECUTE_HOOKS(tpl_event_blr_compile, (plug_info->plugin, connection, transaction, statement, time_millis, req_result)); } void TraceManager::event_blr_execute(TraceConnection* connection, TraceTransaction* transaction, TraceBLRStatement* statement, ntrace_result_t req_result) { EXECUTE_HOOKS(tpl_event_blr_execute, (plug_info->plugin, connection, transaction, statement, req_result)); } void TraceManager::event_dyn_execute(TraceConnection* connection, TraceTransaction* transaction, TraceDYNRequest* request, ntrace_counter_t time_millis, ntrace_result_t req_result) { EXECUTE_HOOKS(tpl_event_dyn_execute, (plug_info->plugin, connection, transaction, request, time_millis, req_result)); } void TraceManager::event_service_attach(TraceService* service, ntrace_result_t att_result) { EXECUTE_HOOKS(tpl_event_service_attach, (plug_info->plugin, service, att_result)); } void TraceManager::event_service_start(TraceService* service, size_t switches_length, const char* switches, ntrace_result_t start_result) { EXECUTE_HOOKS(tpl_event_service_start, (plug_info->plugin, service, switches_length, switches, start_result)); } void TraceManager::event_service_query(TraceService* service, size_t send_item_length, const ntrace_byte_t* send_items, size_t recv_item_length, const ntrace_byte_t* recv_items, ntrace_result_t query_result) { EXECUTE_HOOKS(tpl_event_service_query, (plug_info->plugin, service, send_item_length, send_items, recv_item_length, recv_items, query_result)); } void TraceManager::event_service_detach(TraceService* service, ntrace_result_t detach_result) { EXECUTE_HOOKS(tpl_event_service_detach, (plug_info->plugin, service, detach_result)); } }