Skip to content

Load trace plugins via PluginManager and allow to set requeued plugins for trace session #8707

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 9 commits into from
Aug 21, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 11 additions & 7 deletions doc/README.services_extension
Original file line number Diff line number Diff line change
Expand Up @@ -70,8 +70,9 @@ Start user trace session :
isc_action_svc_trace_start

parameter(s)
isc_spb_trc_name : trace session name, string, optional
isc_spb_trc_cfg : trace session configuration, string, mandatory
isc_spb_trc_name : trace session name, string, optional
isc_spb_trc_cfg : trace session configuration, string, mandatory
isc_spb_trc_plugins : plugins for use with trace session, list of plugin names (valid separators: '\t', ',', ';'), optional

output
text message with status of operation :
Expand Down Expand Up @@ -130,10 +131,11 @@ List of existing trace sessions
output
text messages with list and state of trace sessions
- Session ID: <number>
- name: <string>
- user: <string>
- date: YYYY-MM-DD HH:NN:SS
- flags: <string>
- name: <string>
- user: <string>
- date: YYYY-MM-DD HH:NN:SS
- flags: <string>
- plugins: <string list>

"name" is trace session name and not printed if empty.
"user" is creator user name
Expand All @@ -144,7 +146,9 @@ List of existing trace sessions
if the session was created by the engine itself : "system"
kind of session : "audit" or "trace"
if user session log file is full : "log full"

"plugins" is a set of plugins for use with trace session
Plugins can be specified via API/tracemgr.
By default, plugins form the firebird.conf (TracePlugin) are used

Output of every service is obtained as usually using isc_service_query call
with isc_info_svc_line or isc_info_svc_to_eof information items.
Expand Down
1 change: 1 addition & 0 deletions doc/README.trace_services
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ Action parameters switches :
-N[AME] <string> Session name
-I[D] <number> Session ID
-C[ONFIG] <string> Trace configuration file name
-P[LUGINS] <string list> Plugins list for use with trace session; valid list separators: '\t', ',', ';'

Connection parameters switches :
-SE[RVICE] <string> Service name
Expand Down
1 change: 1 addition & 0 deletions src/common/IntlParametersBlock.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -309,6 +309,7 @@ IntlParametersBlock::TagType IntlSpbStart::checkTag(UCHAR tag, const char** tagN
{
FB_IPB_TAG(isc_spb_trc_name);
FB_IPB_TAG(isc_spb_trc_cfg);
FB_IPB_TAG(isc_spb_trc_plugins);
return TAG_STRING;
}
break;
Expand Down
1 change: 1 addition & 0 deletions src/common/classes/ClumpletReader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -476,6 +476,7 @@ ClumpletReader::ClumpletType ClumpletReader::getClumpletType(UCHAR tag) const
{
case isc_spb_trc_cfg:
case isc_spb_trc_name:
case isc_spb_trc_plugins:
return StringSpb;
case isc_spb_trc_id:
return IntSpb;
Expand Down
1 change: 1 addition & 0 deletions src/include/firebird/impl/consts_pub.h
Original file line number Diff line number Diff line change
Expand Up @@ -657,6 +657,7 @@
#define isc_spb_trc_id 1
#define isc_spb_trc_name 2
#define isc_spb_trc_cfg 3
#define isc_spb_trc_plugins 4

/******************************************/
/* Array slice description language (SDL) */
Expand Down
2 changes: 2 additions & 0 deletions src/include/firebird/impl/msg/fbtracemgr.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,3 +38,5 @@ FB_IMPL_MSG(FBTRACEMGR, 37, trace_switch_user_only, -901, "00", "000", "switch \
FB_IMPL_MSG(FBTRACEMGR, 38, trace_switch_param_miss, -901, "00", "000", "mandatory parameter \"@1\" for switch \"@2\" is missing")
FB_IMPL_MSG(FBTRACEMGR, 39, trace_param_act_notcompat, -901, "00", "000", "parameter \"@1\" is incompatible with action \"@2\"")
FB_IMPL_MSG(FBTRACEMGR, 40, trace_mandatory_switch_miss, -901, "00", "000", "mandatory switch \"@1\" is missing")
FB_IMPL_MSG_NO_SYMBOL(FBTRACEMGR, 41, " -P[LUGINS] <string list> Plugins list for use with trace session; valid list separators: <tab>, <comma>, <semicolon>")
FB_IMPL_MSG_NO_SYMBOL(FBTRACEMGR, 42, " fbtracemgr -SE service_mgr -START -NAME my_trace -CONFIG my_cfg.txt -PLUGINS fbtrace,custom_plugin")
1 change: 1 addition & 0 deletions src/include/gen/Firebird.pas
Original file line number Diff line number Diff line change
Expand Up @@ -4418,6 +4418,7 @@ IProfilerStatsImpl = class(IProfilerStats)
isc_spb_trc_id = byte(1);
isc_spb_trc_name = byte(2);
isc_spb_trc_cfg = byte(3);
isc_spb_trc_plugins = byte(4);
isc_sdl_version1 = byte(1);
isc_sdl_eoc = byte(255);
isc_sdl_relation = byte(2);
Expand Down
1 change: 1 addition & 0 deletions src/jrd/svc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3134,6 +3134,7 @@ bool Service::process_switches(ClumpletReader& spb, string& switches)
{
case isc_spb_trc_cfg:
case isc_spb_trc_name:
case isc_spb_trc_plugins:
get_action_svc_string(spb, switches);
break;
case isc_spb_trc_id:
Expand Down
46 changes: 40 additions & 6 deletions src/jrd/trace/TraceCmdLine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,21 @@ namespace
printMsg(number, dummy, newLine);
}

struct MessageSet
{
const int range[2];
const std::initializer_list<int> list;

void print() const
{
for (int i = range[0]; i <= range[1]; ++i)
printMsg(i);

for (auto id = list.begin(); id != list.end(); ++id)
printMsg(*id);
}
};

[[noreturn]] void usage(UtilSvc* uSvc, const ISC_STATUS code, const char* msg1 = NULL, const char* msg2 = NULL)
{
if (uSvc->isService())
Expand Down Expand Up @@ -94,16 +109,14 @@ namespace

// If the items aren't contiguous, a scheme like in nbackup.cpp will have to be used.
// ASF: This is message codes!
constexpr int MAIN_USAGE[] = {3, 21};
constexpr int EXAMPLES[] = {22, 27};
const MessageSet MAIN_USAGE{{3, 21}, {41}};
const MessageSet EXAMPLES{{22, 27}, {42}};
constexpr int NOTES[] = {28, 29};

for (int i = MAIN_USAGE[0]; i <= MAIN_USAGE[1]; ++i)
printMsg(i);
MAIN_USAGE.print();

printf("\n");
for (int i = EXAMPLES[0]; i <= EXAMPLES[1]; ++i)
printMsg(i);
EXAMPLES.print();

printf("\n");
for (int i = NOTES[0]; i <= NOTES[1]; ++i)
Expand Down Expand Up @@ -253,6 +266,27 @@ void fbtrace(UtilSvc* uSvc, TraceSvcIntf* traceSvc)
usage(uSvc, isc_trace_param_val_miss, sw->in_sw_name);
break;

case IN_SW_TRACE_PLUGINS:
switch (action_sw->in_sw)
{
case IN_SW_TRACE_STOP:
case IN_SW_TRACE_SUSPEND:
case IN_SW_TRACE_RESUME:
case IN_SW_TRACE_LIST:
usage(uSvc, isc_trace_param_act_notcompat, sw->in_sw_name, action_sw->in_sw_name);
break;
}

if (!session.ses_plugins.empty())
usage(uSvc, isc_trace_switch_once, sw->in_sw_name);

itr++;
if (itr < argc && argv[itr])
session.ses_plugins = argv[itr];
else
usage(uSvc, isc_trace_param_val_miss, sw->in_sw_name);
break;

default:
fb_assert(false);
}
Expand Down
32 changes: 15 additions & 17 deletions src/jrd/trace/TraceConfigStorage.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -676,6 +676,9 @@ ULONG ConfigStorage::getSessionSize(const TraceSession& session) noexcept
if ((len = session.ses_logfile.length()))
ret += sz + len;

if ((len = session.ses_plugins.length()))
ret += sz + len;

ret += sz + sizeof(session.ses_start);

return ret;
Expand Down Expand Up @@ -717,25 +720,16 @@ void ConfigStorage::addSession(TraceSession& session)
char* p = reinterpret_cast<char*> (header) + slot->offset;
Writer writer(p, slot->size);

if (!session.ses_name.empty()) {
writer.write(tagName, session.ses_name.length(), session.ses_name.c_str());
}
if (session.ses_auth.hasData()) {
writer.writeData(tagName, session.ses_name);
if (session.ses_auth.hasData())
writer.write(tagAuthBlock, session.ses_auth.getCount(), session.ses_auth.begin());
}
if (!session.ses_user.empty()) {
writer.write(tagUserName, session.ses_user.length(), session.ses_user.c_str());
}
if (session.ses_role.hasData()) {
writer.write(tagRole, session.ses_role.length(), session.ses_role.c_str());
}
if (!session.ses_config.empty()) {
writer.write(tagConfig, session.ses_config.length(), session.ses_config.c_str());
}
writer.writeData(tagUserName, session.ses_user);
writer.writeData(tagRole, session.ses_role);
writer.writeData(tagConfig, session.ses_config);
writer.write(tagStartTS, sizeof(session.ses_start), &session.ses_start);
if (!session.ses_logfile.empty()) {
writer.write(tagLogFile, session.ses_logfile.length(), session.ses_logfile.c_str());
}
writer.writeData(tagLogFile, session.ses_logfile);
writer.writeData(tagPlugins, session.ses_plugins);

writer.write(tagEnd, 0, NULL);
}

Expand Down Expand Up @@ -841,6 +835,10 @@ bool ConfigStorage::readSession(const TraceCSHeader::Slot* slot, TraceSession& s
p = session.ses_role.getBuffer(len);
break;

case tagPlugins:
p = session.ses_plugins.getBuffer(len);
break;

default:
fb_assert(false);
return false;
Expand Down
9 changes: 9 additions & 0 deletions src/jrd/trace/TraceConfigStorage.h
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,7 @@ class ConfigStorage final : public Firebird::GlobalStorage, public Firebird::Ipc
tagStartTS, // date+time when started
tagLogFile, // log file name, if any
tagRole, // SQL role name, if any
tagPlugins, // trace plugins list, if any
tagEnd
};

Expand Down Expand Up @@ -240,6 +241,14 @@ class ConfigStorage final : public Firebird::GlobalStorage, public Firebird::Ipc

void write(ITEM tag, ULONG len, const void* data);

inline void writeData(const ITEM tag, const Firebird::AbstractString& data)
{
if (data.empty())
return;

write(tag, data.length(), data.c_str());
}

private:
char* m_mem;
const char* const m_end;
Expand Down
74 changes: 19 additions & 55 deletions src/jrd/trace/TraceManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -51,10 +51,6 @@ namespace
namespace Jrd {

GlobalPtr<StorageInstance, InstanceControl::PRIORITY_DELETE_FIRST> TraceManager::storageInstance;
TraceManager::Factories* TraceManager::factories = NULL;
GlobalPtr<RWLock> TraceManager::init_factories_lock;
volatile bool TraceManager::init_factories;


bool TraceManager::check_result(ITracePlugin* plugin, const char* module, const char* function,
bool result)
Expand Down Expand Up @@ -126,51 +122,14 @@ void TraceManager::init()
{
// ensure storage is initialized
getStorage();
load_plugins();
changeNumber = 0;
}

void TraceManager::load_plugins()
{
// Initialize all trace needs to false
trace_needs = 0;

if (init_factories)
return;

WriteLockGuard guard(init_factories_lock, FB_FUNCTION);
if (init_factories)
return;

factories = FB_NEW_POOL(*getDefaultMemoryPool()) TraceManager::Factories(*getDefaultMemoryPool());
for (GetPlugins<ITraceFactory> traceItr(IPluginManager::TYPE_TRACE); traceItr.hasData(); traceItr.next())
{
FactoryInfo info;
info.factory = traceItr.plugin();
info.factory->addRef();
string name(traceItr.name());
name.copyTo(info.name, sizeof(info.name));
factories->add(info);
}

init_factories = true;
changeNumber = 0;
}


void TraceManager::shutdown()
{
if (init_factories)
{
WriteLockGuard guard(init_factories_lock, FB_FUNCTION);

if (init_factories)
{
init_factories = false;
delete factories;
factories = NULL;
}
}

getStorage()->shutdown();
}

Expand Down Expand Up @@ -224,7 +183,7 @@ void TraceManager::update_sessions()
}
else
{
trace_sessions[i].plugin->release();
trace_sessions[i].release();
trace_sessions.remove(i);
}
}
Expand Down Expand Up @@ -366,31 +325,36 @@ void TraceManager::update_session(const TraceSession& session)
}
}

ReadLockGuard guard(init_factories_lock, FB_FUNCTION);
if (!factories)
return;
TraceInitInfoImpl attachInfo(session, attachment, filename);

for (FactoryInfo* info = factories->begin(); info != factories->end(); ++info)
for (GetPlugins<ITraceFactory> traceItr(IPluginManager::TYPE_TRACE, session.getPluginsList());
traceItr.hasData(); traceItr.next())
{
TraceInitInfoImpl attachInfo(session, attachment, filename);
FbLocalStatus status;
ITracePlugin* plugin = info->factory->trace_create(&status, &attachInfo);
ITraceFactory* factory = traceItr.plugin();

ITracePlugin* plugin = factory->trace_create(&status, &attachInfo);
if (plugin)
{
plugin->addRef();
SessionInfo sesInfo;
sesInfo.plugin = plugin;
sesInfo.factory_info = info;
sesInfo.plugin->addRef();

sesInfo.factory = factory;
sesInfo.factory->addRef();

string name(traceItr.name());
name.copyTo(sesInfo.pluginName, sizeof(sesInfo.pluginName));

sesInfo.ses_id = session.ses_id;
trace_sessions.add(sesInfo);

new_needs |= info->factory->trace_needs();
new_needs |= sesInfo.factory->trace_needs();
}
else if (status->getState() & IStatus::STATE_ERRORS)
{
string header;
header.printf("Trace plugin %s returned error on call trace_create.", info->name);
header.printf("Trace plugin %s returned error on call trace_create.", traceItr.name());
iscLogStatus(header.c_str(), &status);
}
}
Expand Down Expand Up @@ -455,13 +419,13 @@ void TraceManager::event_dsql_restart(Attachment* att, jrd_tra* transaction,
while (i < trace_sessions.getCount()) \
{ \
SessionInfo* plug_info = &trace_sessions[i]; \
if (check_result(plug_info->plugin, plug_info->factory_info->name, #METHOD, \
if (check_result(plug_info->plugin, plug_info->pluginName, #METHOD, \
plug_info->plugin->METHOD PARAMS)) \
{ \
i++; /* Move to next plugin */ \
} \
else { \
plug_info->plugin->release(); \
plug_info->release(); \
trace_sessions.remove(i); /* Remove broken plugin from the list */ \
} \
}
Expand Down
Loading
Loading