
{                                                                              }
{  IB_Monitor                                                                  }
{                                                                              }

{***************************************************************}
{                                                               }
{          IB Objects Components and Resources Library          }
{                                                               }
{          Copyright (C) 1996-2007 Jason Wharton                }
{          and Computer Programming Solutions, Mesa, AZ         }
{                                                               }
{  This source code unit is released under the terms of the     }
{  CPS Trustware License.                                       }
{  It may be distributed or deployed as source code or in       }
{  compiled form only in compliance with the terms and          }
{  conditions of the CPS Trustware License which may be         )
{  examined at http://www.ibobjects.com/ibo_trustware.html      )
{                                                               }
{***************************************************************}

{******************************************************************************}
{  CONTRIBUTED MODIFICATIONS                                                   }
{  Additions or modifications listed below using format:                       }
{                                                                              }
{  Joe Citizen <joe@joecitizen.com>                                            }
{  mm/dd/yyyy                                                                  }
{     xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx                 }
{     xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx                 }
{     xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx                 }
{            Copyright (C) 2001 Joe Citizen                                    }
{                                                                              }
{------------------------------------------------------------------------------}
{  Place your credits and information in the top of the section                }
{  below with any other descriptions or explanations desired.                  }
{------------------------------------------------------------------------------}
{                                                                              }
{                                                                              }
{******************************************************************************}

{$INCLUDE IB_Directives.inc}

{: This unit contains the IB_Monitor component.}
unit
  IB_Monitor;

interface

uses
  Classes, Forms, SysUtils, Windows,

  IB_Header,
  IB_Session,
  IB_Components,
  IB_Profiler;

const
{: Used to make output text items consistent.}
IB_ERRCODE = 'ERRCODE';
{: Used to make output text items consistent.}
IB_DB_HANDLE = 'DB_HANDLE';
{: Used to make output text items consistent.}
IB_TR_HANDLE = 'TR_HANDLE';
{: Used to make output text items consistent.}
IB_STMT_HANDLE = 'STMT_HANDLE';

type
{: These are the different categories of API calls that can be selectively
included in the monitor output.

By adding in the monitoring hooks there is a noticable performance
hit due to do the additional processing required.

There are two groups of API hooks used so that the mgRow group can be handled
specially. Thus, if mgRow is not included there will be no performance
degradation on looping through fetches for an IB_Cursor or an IB_Query.

BLOB and ARRAY API calls are not being monitored at this time.}
TIB_MonitorGroups = ( mgConnection,
                      mgTransaction,
                      mgStatement,
                      mgRow,
                      mgBlob,
                      mgArray,
                      mgClientTrace,
                      mgProfile );
{: Composite set of groups for monitoring.}
TIB_MonitorGroupSet = set of TIB_MonitorGroups;

{: These are the different categories of API calls that can be selectively
included in the monitor output when the mgStatement group is included.}
TIB_StatementGroups = ( sgAllocate, sgPrepare, sgDescribe,
                        sgStatementInfo, sgExecute, sgExecuteImmediate,
                        sgServerCursor );
{: Composite set of statement subgroups for monitoring.}
TIB_StatementGroupSet = set of TIB_StatementGroups;

{: Event that gives access to each piece of information generated by the
monitor.}
TIB_MonitorOutput = procedure (       Sender: TObject;
                                const NewString: string ) of object;

{: This component provides a very useful way to monitor how the
Interbase API is being utilized. Most calls to the API can be optionally
traced with all of the information associated with the call displayed.
<br><br>
The way this component was designed makes it so that when the monitor is not
enabled there is absolutely no performance degradation. A batch of intercept
API hooks are defined and if a monitor instance exists and it is active then
these intercepts are given to the IB_Session which are in turn used by all of
the components in IB Objects.
<br><br>
When the intercept hooks are taken out then the appplication again has a
direct connection to the API exports. Thus, there is no performance hit in
order to provide monitoring functionality when monitoring is not enabled.
<br><br>
This is also very useful as a security feature. If your application EXE is
built with the monitoring capabilities used in development turned off there
is no way its API activity can be tapped into from an outside process.
<br><br>
These intercept hooks are broken into two categories for more refinement on
performance issues and monitoring. You can monitor things like the SQL
statements getting prepared but the actual calls to isc_dsql_fetch() will
still be processed directly to the IB API without even knowing that
monitoring is going on.}
TIB_Monitor = class(TIB_Component)
private
  FEnabled: boolean;
  FMonitorGroups: TIB_MonitorGroupSet;
  FStatementGroups: TIB_StatementGroupSet;
  FIncludeTimeStamp: boolean;
  FTicks: DWORD;
  FMinTicks: DWORD;
  FOnMonitorOutputItem: TIB_MonitorOutput;
  FItem: string;
  FItemStart: string;
  FItemEnd: string;
  FClientTraceStart: string;
  FClientTraceEnd: string;
  FProfileInfoStart: string;
  FProfileInfoEnd: string;
  FIB_Profiler: TIB_Profiler;
  FShowColOwner: boolean;
  FErrCode: longint;
  FNewLineText: string;
  function IsItemStartStored: boolean;
  function IsItemEndStored: boolean;
  function GetProfilerConnection: TIB_Connection;
  procedure SetProfilerConnection( Value: TIB_Connection );
protected
  procedure SetEnabled( AValue: boolean ); virtual;
  procedure StartItem;
  procedure AppendItem( NewString: string );
  procedure SysShowRowsAffected( RowsAffected: TIB_RowsAffected );
  procedure EndItem( errcode: isc_long );
  procedure ClientTrace( const AString: string );
  procedure ProfileInfo( const AComment: string );
  procedure SetMonitorGroups( AValue: TIB_MonitorGroupSet );
public
  constructor Create( AOwner: TComponent ); override;
  destructor Destroy; override;
{: This property tells the errcode value of the API call just made.}
  property ErrCode: longint read FErrCode;

published
{: Turns monitoring on and off.

When disabled performance returns to normal.}
  property Enabled: boolean read FEnabled write SetEnabled default false;
{: Allows a custom item separator to be supplied.}
  property ItemStart: string read FItemStart
                             write FItemStart
                             stored IsItemStartStored;
{: Include the date and time stamp in the monitor output.}
  property IncludeTimeStamp: boolean read FIncludeTimeStamp
                                     write FIncludeTimeStamp
                                     default false;
{: Allows a custom item separator to be supplied.}
  property ItemEnd: string read FItemEnd
                           write FItemEnd
                           stored IsItemEndStored;
{: The minimum number of ticks before the SECONDS entry is included in the
output item.}
  property MinTicks: DWORD read FMinTicks write FMinTicks;
{: Determines which API call groups that will be monitored.
<br><br>
The mgRow group has its own separate group of hooks such that performance
should remain unaffected for the fetching of records when this group is not
included.}
  property MonitorGroups: TIB_MonitorGroupSet
      read FMonitorGroups
     write SetMonitorGroups
   default [ mgConnection, mgTransaction, mgStatement ];
{: Determine the text that will produce a new line.}
  property NewLineText: string read FNewLineText write FNewLineText;

{: Determines whether or not the owner of the column is shown in the list of
columns and parameters on statements being prepared, described or executed.}
  property ShowColOwner: boolean read FShowColOwner
                                 write FShowColOwner
                                 default false;
{: Determines which API calls in the Statement group that will be
monitored.}
  property StatementGroups: TIB_StatementGroupSet
      read FStatementGroups
     write FStatementGroups
   default [ sgPrepare, sgExecute, sgExecuteImmediate ];
{: Tells how many ticks were used to process the API call. }
  property Ticks: DWORD read FTicks;
{: Event used to handle the output items of the monitor.}
  property OnMonitorOutputItem: TIB_MonitorOutput
      read FOnMonitorOutputItem
     write FOnMonitorOutputItem;
{: This property determines the connection that profiling information is
gathered for.}
  property ProfilerConnection: TIB_Connection
      read GetProfilerConnection
     write SetProfilerConnection;
end;

implementation

uses
  IB_Parse;

var
  FMonitorHooksIn: boolean;
  FRowMonitorHooksIn: boolean;
  Unit_MonitorList: TList;
  Unit_Enabled_Count: integer;
  Unit_Row_Count: integer;

  procedure UpdateMonitorHooks; forward;
  procedure TakeOutRowMonitorHooks; forward;
  procedure TakeOutMonitorHooks; forward;
  procedure PutInRowMonitorHooks; forward;
  procedure PutInMonitorHooks; forward;

constructor TIB_Monitor.Create( AOwner: TComponent );
begin
  inherited Create( AOwner );
  FIB_Profiler := TIB_Profiler.Create( Self );
  FIB_Profiler.IncludeTimeStamp := false;
  EnterCriticalSection( GlobalCS );
  try
    if not Assigned( Unit_MonitorList ) then
      Unit_MonitorList := TList.Create;
    Unit_MonitorList.Add( Self );
  finally;
    LeaveCriticalSection( GlobalCS );
  end;
  FMonitorGroups := [ mgConnection, mgTransaction, mgStatement ];
  FStatementGroups := [ sgPrepare, sgExecute, sgExecuteImmediate ];
  FItemStart := '/*---';
  FItemEnd   := '----*/';
  FClientTraceStart := '/*===';
  FClientTraceEnd   := '====*/';
  FProfileInfoStart := '/*===';
  FProfileInfoEnd   := '====*/';
  FIncludeTimeStamp := false;
  FMinTicks := 10;
  FNewLineText := #13#10;
end;

destructor TIB_Monitor.Destroy;
begin
  try
    Enabled := false;
  except
  end;
  EnterCriticalSection( GlobalCS );
  try
    Unit_MonitorList.Remove( Self );
    if Unit_MonitorList.Count = 0 then
    begin
      Unit_MonitorList.Free;
      Unit_MonitorList := nil;
    end;
  finally;
    LeaveCriticalSection( GlobalCS );
  end;
  inherited Destroy;
end;

function TIB_Monitor.GetProfilerConnection: TIB_Connection;
begin
  Result := FIB_Profiler.IB_Connection;
end;

procedure TIB_Monitor.SetProfilerConnection( Value: TIB_Connection );
begin
  FIB_Profiler.IB_Connection := Value;
end;

function TIB_Monitor.IsItemStartStored: boolean;
begin
  Result := ItemStart <> '/*---';
end;

function TIB_Monitor.IsItemEndStored: boolean;
begin
  Result := ItemEnd <> '---*/';
end;

procedure TIB_Monitor.StartItem;
begin
  FItem := ItemStart + NewLineText;
  if IncludeTimeStamp then begin
    FItem := FItem + '[ ' + FormatDateTime( '', now ) + ' ]' + NewLineText;
  end;
  FTicks := GetTickCount;
end;

procedure TIB_Monitor.AppendItem( NewString: string );
begin
  FItem := FItem + NewString + NewLineText;
end;

procedure TIB_Monitor.EndItem( errcode: isc_long );
begin
  FTicks := GetTickCount - FTicks;
  FErrCode := errcode;
  if Ticks >= MinTicks then begin
    AppendItem( '' );
    AppendItem( 'SECONDS = ' + FormatFloat( '#######0.000', ticks / 1000 ));
  end;
  if errcode <> 0 then begin
    AppendItem( '' );
    AppendItem( IB_ERRCODE + ' = '   + IntToStr( errcode ));
  end;
  FItem := FItem + ItemEnd;
  if Assigned( FOnMonitorOutputItem ) then begin
    FOnMonitorOutputItem( Self, FItem );
  end;
end;

procedure TIB_Monitor.ClientTrace( const AString: string );
begin
  FItem := FClientTraceStart + NewLineText;
  if IncludeTimeStamp then begin
    FItem := FItem + '[ ' + FormatDateTime( '', now ) + ' ]' + NewLineText;
  end;
  FItem := FItem + AString + NewLineText + FClientTraceEnd;
  if Assigned( FOnMonitorOutputItem ) then begin
    FOnMonitorOutputItem( Self, FItem );
  end;
end;

procedure TIB_Monitor.ProfileInfo( const AComment: string );
var
  TmpInfo: string;
  S: string;
begin
  TmpInfo := FProfileInfoStart + NewLineText;
  if IncludeTimeStamp then
    TmpInfo := TmpInfo + '[ ' + FormatDateTime( '', now ) + ' ]' + NewLineText;
  try
    S := FIB_Profiler.Report( AComment, true );
  except
    on E: Exception do
    begin
      S := 'PROFILE ERROR -- EXCEPTION RAISED' + NewLineText + Trim(E.Message) +
           NewLineText;
    end;
  end;
  TmpInfo := TmpInfo + S + FProfileInfoEnd;
  if Assigned( FOnMonitorOutputItem ) then
    FOnMonitorOutputItem( Self, TmpInfo );
end;

procedure TIB_Monitor.SysShowRowsAffected( RowsAffected: TIB_RowsAffected );
var
  wasadded: boolean;
begin
  with RowsAffected do begin
    wasadded := false;
    if ( SelectCount > 0 ) then begin
      AppendItem( '' );
      wasadded := true;
      AppendItem( 'SELECT COUNT: ' + IntToStr( SelectCount ));
    end;
    if ( InsertCount > 0 ) then begin
      if not wasadded then begin
        AppendItem( '' );
        wasadded := true;
      end;
      AppendItem( 'INSERT COUNT: ' + IntToStr( InsertCount ));
    end;
    if ( UpdateCount > 0 ) then begin
      if not wasadded then begin
        AppendItem( '' );
        wasadded := true;
      end;
      AppendItem( 'UPDATE COUNT: ' + IntToStr( UpdateCount ));
    end;
    if ( DeleteCount > 0 ) then begin
      if not wasadded then begin
        AppendItem( '' );
      end;
      AppendItem( 'DELETE COUNT: ' + IntToStr( DeleteCount ));
    end;
  end;
end;

procedure TIB_Monitor.SetEnabled( AValue: boolean );
begin
  if Enabled <> AValue then
  begin
    FEnabled := AValue;
    if Enabled then
      InterlockedIncrement( Unit_Enabled_Count )
    else
      InterlockedDecrement( Unit_Enabled_Count );
    UpdateMonitorHooks;
  end;
end;

procedure TIB_Monitor.SetMonitorGroups( AValue: TIB_MonitorGroupSet );
var
  WasRowMonitored: boolean;
begin
  if MonitorGroups <> AValue then begin
    WasRowMonitored := mgRow in MonitorGroups;
    FMonitorGroups := AValue;
    if ( mgRow in MonitorGroups ) <> WasRowMonitored then begin
      if WasRowMonitored then begin
        Dec( Unit_Row_Count );
      end else begin
        Inc( Unit_Row_Count );
      end;
    end;
    UpdateMonitorHooks;
  end;
end;

procedure UpdateMonitorHooks;
var
  CanMonitorRow: boolean;
  ii: integer;
begin
  EnterCriticalSection( GlobalCS );
  try
    if Unit_Enabled_Count = 0 then
    begin
      if FMonitorHooksIn    then TakeOutMonitorHooks;
      if FRowMonitorHooksIn then TakeOutRowMonitorHooks;
    end
    else
    begin
      if not FMonitorHooksIn then PutInMonitorHooks;
      CanMonitorRow := false;
      if Unit_Row_Count > 0 then
        for ii := 0 to Unit_MonitorList.Count - 1 do
          with TIB_Monitor(Unit_MonitorList.Items[ ii ]) do
            if Enabled then
            begin
              if mgRow in MonitorGroups then
              begin
                CanMonitorRow := true;
                Break;
              end;
            end;
      if CanMonitorRow then
      begin
        if not FRowMonitorHooksIn then PutInRowMonitorHooks;
      end
      else
      if FRowMonitorHooksIn then TakeOutRowMonitorHooks;
    end;
  finally;
    LeaveCriticalSection( GlobalCS );
  end;
end;

procedure TakeOutRowMonitorHooks;
begin
  RevertToOriginalHooks;
  FRowMonitorHooksIn := false;
  if FMonitorHooksIn then PutInMonitorHooks;
end;

procedure TakeOutMonitorHooks;
begin
  RevertToOriginalHooks;
  FMonitorHooksIn := false;
  if FRowMonitorHooksIn then PutInRowMonitorHooks;
end;

{------------------------------------------------------------------------------}

procedure mon_ClientTrace( const AString: string );
var
  ii: integer;
begin
  EnterCriticalSection( GlobalCS ); try
  for ii := 0 to Unit_MonitorList.Count - 1 do
  begin
    with TIB_Monitor(Unit_MonitorList.Items[ ii ]) do
      if Enabled and ( mgClientTrace in MonitorGroups ) then
        ClientTrace( AString );
  end;
  finally; LeaveCriticalSection( GlobalCS ); end;
end;

procedure mon_Profile( const AComment: string );
var
  ii: integer;
begin
  EnterCriticalSection( GlobalCS ); try
  for ii := 0 to Unit_MonitorList.Count - 1 do
  begin
    with TIB_Monitor(Unit_MonitorList.Items[ ii ]) do
      if Enabled and ( mgProfile in MonitorGroups ) then
        ProfileInfo( AComment );
  end;
  finally; LeaveCriticalSection( GlobalCS ); end;
end;

function mon_attach_database ( status:          pstatus_vector;
                               db_name_len:     short;
                               db_name:         pchar;
                               db_handle:       pisc_db_handle;
                               parm_buffer_len: short;
                               parm_buffer:     pchar
                               ):               isc_status; stdcall;
var
  ii: integer;
begin
  EnterCriticalSection( GlobalCS ); try
  for ii := 0 to Unit_MonitorList.Count - 1 do begin
    with TIB_Monitor(Unit_MonitorList.Items[ ii ]) do if Enabled then begin
    if mgConnection in MonitorGroups then begin
      StartItem;
      AppendItem( 'CONNECT DATABASE ' + db_name );
    end;
    end;
  end;
  finally; LeaveCriticalSection( GlobalCS ); end;
  Result := dll_attach_database ( status,
                                  db_name_len,
                                  db_name,
                                  db_handle,
                                  parm_buffer_len,
                                  parm_buffer );
  EnterCriticalSection( GlobalCS ); try
  for ii := 0 to Unit_MonitorList.Count - 1 do begin
    with TIB_Monitor(Unit_MonitorList.Items[ ii ]) do if Enabled then begin
    if mgConnection in MonitorGroups then begin
      AppendItem( IB_DB_HANDLE + ' = ' + IntToStr( Integer(db_handle^)));
      EndItem( Result );
    end;
    end;
  end;
  finally; LeaveCriticalSection( GlobalCS ); end;
end;

function mon_detach_database ( status:    pstatus_vector;
                               db_handle: pisc_db_handle
                               ):         isc_status; stdcall;
var
  ii: integer;
begin
  EnterCriticalSection( GlobalCS ); try
  for ii := 0 to Unit_MonitorList.Count - 1 do
  begin
    with TIB_Monitor(Unit_MonitorList.Items[ ii ]) do if Enabled then
    begin
    if mgConnection in MonitorGroups then
    begin
      StartItem;
      AppendItem( 'DISCONNECT DATABASE' );
      AppendItem( IB_DB_HANDLE + ' = ' + IntToStr( Integer(db_handle^)));
    end;
    end;
  end;
  finally; LeaveCriticalSection( GlobalCS ); end;
  try
    Result := dll_detach_database ( status, db_handle );
  except
    Result := 0; // Seems to be a problem in GDS32.DLL.
  end;
  EnterCriticalSection( GlobalCS ); try
  for ii := 0 to Unit_MonitorList.Count - 1 do
  begin
    with TIB_Monitor(Unit_MonitorList.Items[ ii ]) do if Enabled then
    begin
    if mgConnection in MonitorGroups then
    begin
      EndItem( Result );
    end;
    end;
  end;
  finally; LeaveCriticalSection( GlobalCS ); end;
end;

function mon_drop_database ( status:    pstatus_vector;
                             db_handle: pisc_db_handle
                             ):         isc_status; stdcall;
var
  ii: integer;
begin
  EnterCriticalSection( GlobalCS ); try
  for ii := 0 to Unit_MonitorList.Count - 1 do begin
    with TIB_Monitor(Unit_MonitorList.Items[ ii ]) do if Enabled then begin
    if mgConnection in MonitorGroups then begin
      StartItem;
      AppendItem( 'DROP DATABASE' );
      AppendItem( IB_DB_HANDLE + ' = ' + IntToStr( Integer(db_handle^)));
    end;
    end;
  end;
  finally; LeaveCriticalSection( GlobalCS ); end;
  Result := dll_drop_database ( status, db_handle );
  EnterCriticalSection( GlobalCS ); try
  for ii := 0 to Unit_MonitorList.Count - 1 do begin
    with TIB_Monitor(Unit_MonitorList.Items[ ii ]) do if Enabled then begin
    if mgConnection in MonitorGroups then begin
      EndItem( Result );
    end;
    end;
  end;
  finally; LeaveCriticalSection( GlobalCS ); end;
end;

function mon_database_info ( status:      pstatus_vector;
                             pdb_handle:  pisc_db_handle;
                             b:           short;
                             c:	          pchar;
                             d:	          short;
                             e:	          pchar
                             ):           isc_status; stdcall;
var
  ii: integer;
begin
  EnterCriticalSection( GlobalCS ); try
  for ii := 0 to Unit_MonitorList.Count - 1 do begin
    with TIB_Monitor(Unit_MonitorList.Items[ ii ]) do if Enabled then begin
    if mgConnection in MonitorGroups then begin
      StartItem;
      AppendItem( 'DATABASE INFO' );
      AppendItem( IB_DB_HANDLE + ' = ' + IntToStr( Integer(pdb_handle^)));
    end;
    end;
  end;
  finally; LeaveCriticalSection( GlobalCS ); end;
  Result := dll_database_info ( status, pdb_handle, b, c, d, e );
  EnterCriticalSection( GlobalCS ); try
  for ii := 0 to Unit_MonitorList.Count - 1 do begin
    with TIB_Monitor(Unit_MonitorList.Items[ ii ]) do if Enabled then begin
    if mgConnection in MonitorGroups then begin
      EndItem( Result );
    end;
    end;
  end;
  finally; LeaveCriticalSection( GlobalCS ); end;
end;

{------------------------------------------------------------------------------}

function mon_commit_retaining ( status:    pstatus_vector;
                                tr_handle: pisc_tr_handle
                                ):         isc_status; stdcall;
var
  ii: integer;
  tmpProfile: boolean;
begin
  tmpProfile := false;
  EnterCriticalSection( GlobalCS ); try
  for ii := 0 to Unit_MonitorList.Count - 1 do begin
    with TIB_Monitor(Unit_MonitorList.Items[ ii ]) do if Enabled then begin
    if mgTransaction in MonitorGroups then begin
      StartItem;
      AppendItem( 'COMMIT RETAINING' );
      AppendItem( IB_TR_HANDLE + ' = ' + IntToStr( Integer(tr_handle^)));
    end;
    end;
  end;
  finally; LeaveCriticalSection( GlobalCS ); end;
  Result := dll_commit_retaining ( status, tr_handle );
  EnterCriticalSection( GlobalCS ); try
  for ii := 0 to Unit_MonitorList.Count - 1 do begin
    with TIB_Monitor(Unit_MonitorList.Items[ ii ]) do if Enabled then begin
    if mgTransaction in MonitorGroups then begin
      EndItem( Result );
      tmpProfile := true;
    end;
    end;
  end;
  finally; LeaveCriticalSection( GlobalCS ); end;
  if tmpProfile then
    mon_Profile( 'After commit retaining' );
end;

function mon_commit_transaction ( status:    pstatus_vector;
                                  tr_handle: pisc_tr_handle
                                  ):         isc_status; stdcall;
var
  ii: integer;
  tmpProfile: boolean;
begin
  tmpProfile := false;
  EnterCriticalSection( GlobalCS ); try
  for ii := 0 to Unit_MonitorList.Count - 1 do begin
    with TIB_Monitor(Unit_MonitorList.Items[ ii ]) do if Enabled then begin
    if mgTransaction in MonitorGroups then begin
      StartItem;
      AppendItem( 'COMMIT' );
      AppendItem( IB_TR_HANDLE + ' = ' + IntToStr( Integer(tr_handle^)));
    end;
    end;
  end;
  finally; LeaveCriticalSection( GlobalCS ); end;
  Result := dll_commit_transaction ( status, tr_handle );
  EnterCriticalSection( GlobalCS ); try
  for ii := 0 to Unit_MonitorList.Count - 1 do begin
    with TIB_Monitor(Unit_MonitorList.Items[ ii ]) do if Enabled then begin
    if mgTransaction in MonitorGroups then begin
      EndItem( Result );
      tmpProfile := true;
    end;
    end;
  end;
  finally; LeaveCriticalSection( GlobalCS ); end;
  if tmpProfile then
    mon_Profile( 'After commit' );
end;

function mon_rollback_transaction ( status:    pstatus_vector;
                                    tr_handle: pisc_tr_handle
                                    ):         isc_status; stdcall;
var
  ii: integer;
begin
  EnterCriticalSection( GlobalCS ); try
  for ii := 0 to Unit_MonitorList.Count - 1 do begin
    with TIB_Monitor(Unit_MonitorList.Items[ ii ]) do if Enabled then begin
    if mgTransaction in MonitorGroups then begin
      StartItem;
      AppendItem( 'ROLLBACK' );
      AppendItem( IB_TR_HANDLE + ' = ' + IntToStr( Integer(tr_handle^)));
    end;
    end;
  end;
  finally; LeaveCriticalSection( GlobalCS ); end;
  Result := dll_rollback_transaction ( status, tr_handle );
  EnterCriticalSection( GlobalCS ); try
  for ii := 0 to Unit_MonitorList.Count - 1 do begin
    with TIB_Monitor(Unit_MonitorList.Items[ ii ]) do if Enabled then begin
    if mgTransaction in MonitorGroups then begin
      EndItem( Result );
    end;
    end;
  end;
  finally; LeaveCriticalSection( GlobalCS ); end;
  mon_Profile( 'After rollback' );
end;

function mon_rollback_retaining ( status:    pstatus_vector;
                                  tr_handle: pisc_tr_handle
                                  ):         isc_status; stdcall;
var
  ii: integer;
begin
  EnterCriticalSection( GlobalCS ); try
  for ii := 0 to Unit_MonitorList.Count - 1 do begin
    with TIB_Monitor(Unit_MonitorList.Items[ ii ]) do if Enabled then begin
    if mgTransaction in MonitorGroups then begin
      StartItem;
      AppendItem( 'ROLLBACK RETAINING' );
      AppendItem( IB_TR_HANDLE + ' = ' + IntToStr( Integer(tr_handle^)));
    end;
    end;
  end;
  finally; LeaveCriticalSection( GlobalCS ); end;
  Result := dll_rollback_retaining ( status, tr_handle );
  EnterCriticalSection( GlobalCS ); try
  for ii := 0 to Unit_MonitorList.Count - 1 do begin
    with TIB_Monitor(Unit_MonitorList.Items[ ii ]) do if Enabled then begin
    if mgTransaction in MonitorGroups then begin
      EndItem( Result );
    end;
    end;
  end;
  finally; LeaveCriticalSection( GlobalCS ); end;
  mon_Profile( 'After rollback retaining' );
end;

function mon_start_multiple ( status:             pstatus_vector;
                              tr_handle:          pisc_tr_handle;
                              db_handle_count:    short;
                              teb_vector_address: pointer
                              ):                  isc_status; stdcall;
var
  ii: integer;
begin
  mon_Profile( 'Before start transaction' );
  EnterCriticalSection( GlobalCS ); try
  for ii := 0 to Unit_MonitorList.Count - 1 do begin
    with TIB_Monitor(Unit_MonitorList.Items[ ii ]) do if Enabled then begin
    if mgTransaction in MonitorGroups then begin
      StartItem;
      AppendItem( 'START TRANSACTION' );
      AppendItem( 'DB HANDLE COUNT ' + IntToStr( db_handle_count ));
    end;
    end;
  end;
  finally; LeaveCriticalSection( GlobalCS ); end;
  Result := dll_start_multiple ( status,
                                 tr_handle,
                                 db_handle_count,
                                 teb_vector_address );
  EnterCriticalSection( GlobalCS ); try
  for ii := 0 to Unit_MonitorList.Count - 1 do begin
    with TIB_Monitor(Unit_MonitorList.Items[ ii ]) do if Enabled then begin
    if mgTransaction in MonitorGroups then begin
      AppendItem( IB_TR_HANDLE + ' = ' + IntToStr( Integer(tr_handle^)));
      EndItem( Result );
    end;
    end;
  end;
  finally; LeaveCriticalSection( GlobalCS ); end;
end;

{------------------------------------------------------------------------------}

function mon_dsql_allocate_statement ( status:    pstatus_vector;
                                       pdb_handle: pisc_db_handle;
                                       pst_handle: pisc_stmt_handle
                                       ):         isc_status; stdcall;
var
  ii: integer;
begin
  EnterCriticalSection( GlobalCS ); try
  for ii := 0 to Unit_MonitorList.Count - 1 do begin
    with TIB_Monitor(Unit_MonitorList.Items[ ii ]) do if Enabled then begin
    if ( mgStatement in MonitorGroups ) and
       ( sgAllocate in StatementGroups ) then begin
      StartItem;
      AppendItem( 'ALLOCATE STATEMENT' );
      AppendItem( IB_DB_HANDLE + ' = ' + IntToStr( Integer(Pdb_handle^)));
    end;
    end;
  end;
  finally; LeaveCriticalSection( GlobalCS ); end;
  Result := dll_dsql_allocate_statement ( status, pdb_handle, pst_handle );
  EnterCriticalSection( GlobalCS ); try
  for ii := 0 to Unit_MonitorList.Count - 1 do begin
    with TIB_Monitor(Unit_MonitorList.Items[ ii ]) do if Enabled then begin
    if ( mgStatement in MonitorGroups ) and
       ( sgAllocate in StatementGroups ) then begin
      AppendItem( IB_STMT_HANDLE + ' = ' + IntToStr( Integer(pst_handle^)));
      EndItem( Result );
    end;
    end;
  end;
  finally; LeaveCriticalSection( GlobalCS ); end;
end;

function mon_dsql_alloc_statement2 ( status:       pstatus_vector;
                                     pdb_handle:   pisc_db_handle;
                                     pstmt_handle: pisc_stmt_handle
                                     ):            isc_status; stdcall;
var
  ii: integer;
begin
  EnterCriticalSection( GlobalCS ); try
  for ii := 0 to Unit_MonitorList.Count - 1 do begin
    with TIB_Monitor(Unit_MonitorList.Items[ ii ]) do if Enabled then begin
    if ( mgStatement in MonitorGroups ) and
       ( sgAllocate in StatementGroups ) then begin
      StartItem;
      AppendItem( 'ALLOCATE STATEMENT 2' );
      AppendItem( IB_DB_HANDLE + ' = ' + IntToStr( Integer(Pdb_handle^)));
    end;
    end;
  end;
  finally; LeaveCriticalSection( GlobalCS ); end;
  Result := dll_dsql_alloc_statement2 ( status, pdb_handle, pstmt_handle );
  EnterCriticalSection( GlobalCS ); try
  for ii := 0 to Unit_MonitorList.Count - 1 do begin
    with TIB_Monitor(Unit_MonitorList.Items[ ii ]) do if Enabled then begin
    if ( mgStatement in MonitorGroups ) and
       ( sgAllocate in StatementGroups ) then begin
      AppendItem( IB_STMT_HANDLE + ' = ' +
                     IntToStr( integer(pstmt_handle^)));
      EndItem( Result );
    end;
    end;
  end;
  finally; LeaveCriticalSection( GlobalCS ); end;
end;

function mon_dsql_free_statement ( status_vector: pstatus_vector;
                                   st_handle:     pisc_stmt_handle;
                                   option:        word
                                   ):             isc_status; stdcall;
var
  ii: integer;
begin
  EnterCriticalSection( GlobalCS ); try
  for ii := 0 to Unit_MonitorList.Count - 1 do begin
    with TIB_Monitor(Unit_MonitorList.Items[ ii ]) do if Enabled then begin
    if ( mgStatement in MonitorGroups ) and
       (( sgServerCursor in StatementGroups ) or
        ( sgAllocate in StatementGroups )) then begin
      StartItem;
      if option = DSQL_CLOSE then begin
        if ( sgServerCursor in StatementGroups ) then
        AppendItem( 'CLOSE CURSOR' );
      end else begin
        if ( sgAllocate in StatementGroups ) then
        AppendItem( 'DEALLOCATE STATEMENT' );
      end;
      AppendItem( IB_STMT_HANDLE + ' = ' + IntToStr( integer(st_handle^)));
    end;
    end;
  end;
  finally; LeaveCriticalSection( GlobalCS ); end;
  Result := dll_dsql_free_statement ( status_vector, st_handle, option );
  EnterCriticalSection( GlobalCS ); try
  for ii := 0 to Unit_MonitorList.Count - 1 do begin
    with TIB_Monitor(Unit_MonitorList.Items[ ii ]) do if Enabled then begin
    if ( mgStatement in MonitorGroups ) and
       ((( sgServerCursor in StatementGroups ) and ( option = DSQL_CLOSE )) or
        (( sgAllocate in StatementGroups ) and ( option <> DSQL_CLOSE ))) then
      EndItem( Result );
    end;
  end;
  finally; LeaveCriticalSection( GlobalCS ); end;
end;

function mon_dsql_prepare ( status:    pstatus_vector;
                            tr_handle: pisc_tr_handle;
                            st_handle: pisc_stmt_handle;
                            len:       word;
                            statement: pchar;
                            dialect:   word;
                            params:    PXSQLDA
                            ):         isc_status; stdcall;
var
  ii: integer;
  FStatementPlan: string;
begin
  mon_Profile( 'Before prepare' );
  EnterCriticalSection( GlobalCS ); try
  for ii := 0 to Unit_MonitorList.Count - 1 do
  begin
    with TIB_Monitor(Unit_MonitorList.Items[ ii ]) do if Enabled then
    begin
    if ( mgStatement in MonitorGroups ) and
       ( sgPrepare   in StatementGroups ) then
    begin
      StartItem;
      AppendItem( 'PREPARE STATEMENT' );
      AppendItem( IB_TR_HANDLE + ' = ' + IntToStr( Integer(tr_handle^)));
      AppendItem( IB_STMT_HANDLE + ' = ' + IntToStr( Integer(st_handle^)));
      AppendItem( '' );
      AppendItem( statement );
    end;
    end;
  end;
  finally; LeaveCriticalSection( GlobalCS ); end;
  Result := dll_dsql_prepare ( status,
                               tr_handle,
                               st_handle,
                               len,
                               statement,
                               dialect,
                               params );
  if Result = 0 then
    FStatementPlan := GetStatementPlanFromHandle( st_handle );
  EnterCriticalSection( GlobalCS ); try
  for ii := 0 to Unit_MonitorList.Count - 1 do
  begin
    with TIB_Monitor(Unit_MonitorList.Items[ ii ]) do if Enabled then
    begin
    if ( mgStatement in MonitorGroups ) and
       ( sgPrepare   in StatementGroups ) then
    begin
      if FStatementPlan <> '' then
      begin
        AppendItem( FStatementPlan );
        AppendItem( '' );
      end;
      AppendItem( 'FIELDS = [ '+ GetParamBuffers( params, ShowColOwner )+' ]' );
      EndItem( Result );
    end;
    end;
  end;
  finally; LeaveCriticalSection( GlobalCS ); end;
  mon_Profile( 'After prepare' );
end;

function mon_dsql_execute ( status:    pstatus_vector;
                            tr_handle: pisc_tr_handle;
                            st_handle: pisc_stmt_handle;
                            dialect:   word;
                            params:    PXSQLDA
                            ):         isc_status; stdcall;
var
  ii: integer;
  rowsaffected: TIB_RowsAffected;
begin
  mon_Profile( 'Before execute' );
  EnterCriticalSection( GlobalCS ); try
  for ii := 0 to Unit_MonitorList.Count - 1 do begin
    with TIB_Monitor(Unit_MonitorList.Items[ ii ]) do if Enabled then begin
    if ( mgStatement in MonitorGroups ) and
       ( sgExecute   in StatementGroups ) then begin
      StartItem;
      AppendItem( 'EXECUTE STATEMENT' );
      AppendItem( IB_TR_HANDLE + ' = ' + IntToStr( Integer(tr_handle^)));
      AppendItem( IB_STMT_HANDLE + ' = ' + IntToStr( Integer(st_handle^)));
    end;
    end;
  end;
  finally; LeaveCriticalSection( GlobalCS ); end;
  Result := dll_dsql_execute ( status,
                               tr_handle,
                               st_handle,
                               dialect,
                               params );
  if Result = 0 then
    RowsAffected := TIB_SessionBase.GetRowsAffected( st_handle );
  EnterCriticalSection( GlobalCS ); try
  for ii := 0 to Unit_MonitorList.Count - 1 do
  begin
    with TIB_Monitor(Unit_MonitorList.Items[ ii ]) do if Enabled then begin
    if ( mgStatement in MonitorGroups ) and
       ( sgExecute   in StatementGroups ) then begin
      AppendItem( 'PARAMS = [ ' +
                  GetParamBuffers( params, ShowColOwner ) +' ]' );
      if Result = 0 then begin
        SysShowRowsAffected( RowsAffected );
      end;
      EndItem( Result );
    end;
    end;
  end;
  finally; LeaveCriticalSection( GlobalCS ); end;
  mon_Profile( 'After execute' );
end;

function mon_dsql_execute2 ( status:    pstatus_vector;
                             tr_handle: pisc_tr_handle;
                             st_handle: pisc_stmt_handle;
                             a:         word;
                             inparams:  PXSQLDA;
                             outparams: PXSQLDA
                             ):         isc_status; stdcall;
var
  ii: integer;
  rowsaffected: TIB_RowsAffected;
begin
  mon_Profile( 'Before execute2 dsql' );
  EnterCriticalSection( GlobalCS ); try
  for ii := 0 to Unit_MonitorList.Count - 1 do begin
    with TIB_Monitor(Unit_MonitorList.Items[ ii ]) do if Enabled then begin
    if ( mgStatement in MonitorGroups ) and
       ( sgExecute in StatementGroups ) then begin
      StartItem;
      AppendItem( 'EXECUTE2 DSQL' );
      AppendItem( IB_TR_HANDLE + ' = ' + IntToStr( Integer(tr_handle^)));
      AppendItem( IB_STMT_HANDLE + ' = ' + IntToStr( Integer(st_handle^)));
    end;
    end;
  end;
  finally; LeaveCriticalSection( GlobalCS ); end;
  Result := dll_dsql_execute2 ( status,
                                tr_handle,
                                st_handle,
                                a,
                                inparams,
                                outparams );
  if Result = 0 then
    RowsAffected := TIB_SessionBase.GetRowsAffected( st_handle );
  EnterCriticalSection( GlobalCS ); try
  for ii := 0 to Unit_MonitorList.Count - 1 do begin
    with TIB_Monitor(Unit_MonitorList.Items[ ii ]) do if Enabled then begin
    if ( mgStatement in MonitorGroups ) and
       ( sgExecute in StatementGroups ) then begin
      AppendItem( 'PARAMS = [ ' +
                  GetParamBuffers( inparams, ShowColOwner ) +' ]');
      AppendItem( 'FIELDS = [ ' +
                  GetParamBuffers( outparams, ShowColOwner) +' ]');
      if Result = 0 then begin
        SysShowRowsAffected( RowsAffected );
      end;
      EndItem( Result );
    end;
    end;
  end;
  finally; LeaveCriticalSection( GlobalCS ); end;
  mon_Profile( 'After execute2 dsql' );
end;

function mon_dsql_execute_immediate ( status:    pstatus_vector;
                                      db_handle: pisc_db_handle;
                                      tr_handle: pisc_tr_handle;
                                      length:    word;
                                      statement: PChar;
                                      dialect:   word;
                                      params:    PXSQLDA
                                      ):         isc_status; stdcall;
var
  ii: integer;
begin
  mon_Profile( 'Before execute immediate' );
  EnterCriticalSection( GlobalCS ); try
  for ii := 0 to Unit_MonitorList.Count - 1 do begin
    with TIB_Monitor(Unit_MonitorList.Items[ ii ]) do if Enabled then begin
    if ( mgStatement in MonitorGroups ) and
       ( sgExecuteImmediate in StatementGroups ) then begin
      StartItem;
      AppendItem( 'EXECUTE IMMEDIATE' );
      AppendItem( IB_DB_HANDLE + ' = ' + IntToStr( Integer(db_handle^)));
      AppendItem( IB_TR_HANDLE + ' = ' + IntToStr( Integer(tr_handle^)));
      AppendItem( '' );
      AppendItem( statement );
      AppendItem( '' );
    end;
    end;
  end;
  finally; LeaveCriticalSection( GlobalCS ); end;
  Result := dll_dsql_execute_immediate ( status,
                                         db_handle,
                                         tr_handle,
                                         length,
                                         statement,
                                         dialect,
                                         params );
  EnterCriticalSection( GlobalCS ); try
  for ii := 0 to Unit_MonitorList.Count - 1 do begin
    with TIB_Monitor(Unit_MonitorList.Items[ ii ]) do if Enabled then begin
    if ( mgStatement in MonitorGroups ) and
       ( sgExecuteImmediate in StatementGroups ) then begin
      AppendItem( 'PARAMS = [ ' +
                  GetParamBuffers( params, ShowColOwner ) +' ]' );
      EndItem( Result );
    end;
    end;
  end;
  finally; LeaveCriticalSection( GlobalCS ); end;
  mon_Profile( 'After execute immediate' );
end;

function mon_dsql_exec_immed2 ( status:    pstatus_vector;
                                db_handle: pisc_db_handle;
                                tr_handle: pisc_tr_handle;
                                length:    word;
                                statement: PChar;
                                dialect:   word;
                                params:    PXSQLDA;
                                outvar:    PXSQLDA
                                ):         isc_status; stdcall;
var
  ii: integer;
begin
  mon_Profile( 'Before execute immediate2' );
  EnterCriticalSection( GlobalCS ); try
  for ii := 0 to Unit_MonitorList.Count - 1 do begin
    with TIB_Monitor(Unit_MonitorList.Items[ ii ]) do if Enabled then begin
    if ( mgStatement in MonitorGroups ) and
       ( sgExecuteImmediate in StatementGroups ) then begin
      StartItem;
      AppendItem( 'EXECUTE IMMEDIATE 2' );
      AppendItem( IB_DB_HANDLE + ' = ' + IntToStr( Integer(db_handle^)));
      AppendItem( IB_TR_HANDLE + ' = ' + IntToStr( Integer(tr_handle^)));
      AppendItem( '' );
      AppendItem( statement );
      AppendItem( '' );
    end;
    end;
  end;
  finally; LeaveCriticalSection( GlobalCS ); end;
  Result := dll_dsql_exec_immed2 ( status,
                                   db_handle,
                                   tr_handle,
                                   length,
                                   statement,
                                   dialect,
                                   params,
                                   outvar );
  EnterCriticalSection( GlobalCS ); try
  for ii := 0 to Unit_MonitorList.Count - 1 do begin
    with TIB_Monitor(Unit_MonitorList.Items[ ii ]) do if Enabled then begin
    if ( mgStatement in MonitorGroups ) and
       ( sgExecuteImmediate in StatementGroups ) then begin
      AppendItem( 'PARAMS = [ ' +
                  GetParamBuffers( params, ShowColOwner ) +' ]' );
      AppendItem( 'FIELDS = [ ' +
                  GetParamBuffers( outvar, ShowColOwner ) +' ]' );
      EndItem( Result );
    end;
    end;
  end;
  finally; LeaveCriticalSection( GlobalCS ); end;
  mon_Profile( 'After execute immediate2' );
end;

function mon_dsql_set_cursor_name ( status_vector: pstatus_vector;
                                    st_handle:     pisc_stmt_handle;
                                    cursor_name:   PChar;
                                    unusedType:    word
                                    ):             isc_status; stdcall;
var
  ii: integer;
begin
  EnterCriticalSection( GlobalCS ); try
  for ii := 0 to Unit_MonitorList.Count - 1 do begin
    with TIB_Monitor(Unit_MonitorList.Items[ ii ]) do if Enabled then begin
    if ( mgStatement in MonitorGroups ) and
       ( sgServerCursor  in StatementGroups ) then begin
      StartItem;
      AppendItem( 'OPEN CURSOR' );
      AppendItem( IB_STMT_HANDLE + ' = ' + IntToStr( Integer(st_handle^)));
      AppendItem( 'NAME = ' + cursor_name );
      AppendItem( '' );
    end;
    end;
  end;
  finally; LeaveCriticalSection( GlobalCS ); end;
  Result := dll_dsql_set_cursor_name ( status_vector,
                                       st_handle,
                                       cursor_name,
                                       unusedType );
  EnterCriticalSection( GlobalCS ); try
  for ii := 0 to Unit_MonitorList.Count - 1 do begin
    with TIB_Monitor(Unit_MonitorList.Items[ ii ]) do if Enabled then begin
    if ( mgStatement in MonitorGroups ) and
       ( sgServerCursor in StatementGroups ) then begin
      EndItem( Result );
    end;
    end;
  end;
  finally; LeaveCriticalSection( GlobalCS ); end;
end;

function mon_dsql_describe ( status_vector: pstatus_vector;
                             st_handle:     pisc_stmt_handle;
                             dialect:       word;
                             params:        PXSQLDA
                             ):             isc_status; stdcall;
var
  ii: integer;
begin
  EnterCriticalSection( GlobalCS ); try
  for ii := 0 to Unit_MonitorList.Count - 1 do begin
    with TIB_Monitor(Unit_MonitorList.Items[ ii ]) do if Enabled then begin
    if ( mgStatement in MonitorGroups ) and
       ( sgDescribe in StatementGroups ) then begin
      StartItem;
      AppendItem( 'DESCRIBE OUTPUT' );
      AppendItem( IB_STMT_HANDLE + ' = ' + IntToStr( Integer(st_handle^)));
    end;
    end;
  end;
  finally; LeaveCriticalSection( GlobalCS ); end;
  Result := dll_dsql_describe ( status_vector,
                                st_handle,
                                dialect,
                                params );
  EnterCriticalSection( GlobalCS ); try
  for ii := 0 to Unit_MonitorList.Count - 1 do begin
    with TIB_Monitor(Unit_MonitorList.Items[ ii ]) do if Enabled then begin
    if ( mgStatement in MonitorGroups ) and
       ( sgDescribe in StatementGroups ) then begin
      AppendItem( 'FIELDS = [ ' +
                  GetParamBuffers( params, ShowColOwner ) +' ]' );
      EndItem( Result );
    end;
    end;
  end;
  finally; LeaveCriticalSection( GlobalCS ); end;
end;

function mon_dsql_describe_bind ( status:        pstatus_vector;
                                  st_handle:     pisc_stmt_handle;
                                  dialect:       word;
                                  params:        PXSQLDA
                                  ):             isc_status; stdcall;
var
  ii: integer;
begin
  EnterCriticalSection( GlobalCS ); try
  for ii := 0 to Unit_MonitorList.Count - 1 do begin
    with TIB_Monitor(Unit_MonitorList.Items[ ii ]) do if Enabled then begin
    if ( mgStatement in MonitorGroups ) and
       ( sgDescribe in StatementGroups ) then begin
      StartItem;
      AppendItem( 'DESCRIBE INPUT' );
      AppendItem( IB_STMT_HANDLE + ' = ' + IntToStr( Integer(st_handle^)));
    end;
    end;
  end;
  finally; LeaveCriticalSection( GlobalCS ); end;
  Result := dll_dsql_describe_bind ( status,
                                     st_handle,
                                     dialect,
                                     params );
  EnterCriticalSection( GlobalCS ); try
  for ii := 0 to Unit_MonitorList.Count - 1 do begin
    with TIB_Monitor(Unit_MonitorList.Items[ ii ]) do if Enabled then begin
    if ( mgStatement in MonitorGroups ) and
       ( sgDescribe in StatementGroups ) then begin
      AppendItem( 'PARAMS = [ ' +
                  GetParamBuffers( params, ShowColOwner ) +' ]' );
      EndItem( Result );
    end;
    end;
  end;
  finally; LeaveCriticalSection( GlobalCS ); end;
end;

function mon_dsql_sql_info ( status:        pstatus_vector;
                             st_handle:     pisc_stmt_handle;
                             item_length:   short;
                             items:         pchar;
                             buffer_length: short;
                             result_buffer: pchar
                             ):             isc_status; stdcall;
var
  ii: integer;
begin
  EnterCriticalSection( GlobalCS ); try
  for ii := 0 to Unit_MonitorList.Count - 1 do begin
    with TIB_Monitor(Unit_MonitorList.Items[ ii ]) do if Enabled then begin
    if ( mgStatement in MonitorGroups ) and
       ( sgStatementInfo in StatementGroups ) then begin
      StartItem;
      AppendItem( 'STATEMENT INFO' );
      AppendItem( IB_STMT_HANDLE + ' = ' + IntToStr( Integer(st_handle^)));
    end;
    end;
  end;
  finally; LeaveCriticalSection( GlobalCS ); end;
  Result := dll_dsql_sql_info ( status,
                                st_handle,
                                item_length,
                                items,
                                buffer_length,
                                result_buffer );
  EnterCriticalSection( GlobalCS ); try
  for ii := 0 to Unit_MonitorList.Count - 1 do begin
    with TIB_Monitor(Unit_MonitorList.Items[ ii ]) do if Enabled then begin
    if ( mgStatement in MonitorGroups ) and
       ( sgStatementInfo in StatementGroups ) then begin
      EndItem( Result );
    end;
    end;
  end;
  finally; LeaveCriticalSection( GlobalCS ); end;
end;

{------------------------------------------------------------------------------}

function mon_dsql_fetch ( status_vector: pstatus_vector;
                          st_handle:     pisc_stmt_handle;
                          dialect:       word;
                          params:        PXSQLDA
                          ):             isc_status; stdcall;
var
  ii: integer;
begin
  EnterCriticalSection( GlobalCS ); try
  for ii := 0 to Unit_MonitorList.Count - 1 do begin
    with TIB_Monitor(Unit_MonitorList.Items[ ii ]) do if Enabled then begin
      StartItem;
      AppendItem( 'FETCH' );
      AppendItem( IB_STMT_HANDLE + ' = ' + IntToStr( Integer(st_handle^)));
    end;
  end;
  finally; LeaveCriticalSection( GlobalCS ); end;
  Result := dll_dsql_fetch ( status_vector,
                             st_handle,
                             dialect,
                             params );
  EnterCriticalSection( GlobalCS ); try
  for ii := 0 to Unit_MonitorList.Count - 1 do begin
    with TIB_Monitor(Unit_MonitorList.Items[ ii ]) do if Enabled then begin
      AppendItem( 'FIELDS = [ ' +
                  GetParamBuffers( params, ShowColOwner ) +' ]' );
      EndItem( Result );
    end;
  end;
  finally; LeaveCriticalSection( GlobalCS ); end;
end;

function mon_dsql_row_fetch ( status:    pstatus_vector;
                              tr_handle: pisc_tr_handle;
                              st_handle: pisc_stmt_handle;
                              dialect:   word;
                              params:    PXSQLDA
                              ):         isc_status; stdcall;
var
  ii: integer;
begin
  EnterCriticalSection( GlobalCS ); try
  for ii := 0 to Unit_MonitorList.Count - 1 do begin
    with TIB_Monitor(Unit_MonitorList.Items[ ii ]) do if Enabled then begin
      StartItem;
      AppendItem( 'EXECUTE ROW FETCH' );
      AppendItem( IB_TR_HANDLE + ' = ' + IntToStr( Integer(tr_handle^)));
      AppendItem( IB_STMT_HANDLE + ' = ' + IntToStr( Integer(st_handle^)));
    end;
  end;
  finally; LeaveCriticalSection( GlobalCS ); end;
  Result := dll_dsql_execute ( status,
                               tr_handle,
                               st_handle,
                               dialect,
                               params );
  EnterCriticalSection( GlobalCS ); try
  for ii := 0 to Unit_MonitorList.Count - 1 do begin
    with TIB_Monitor(Unit_MonitorList.Items[ ii ]) do if Enabled then begin
      AppendItem( 'PARAMS = [ ' +
                  GetParamBuffers( params, ShowColOwner ) + ' ]' );
      EndItem( Result );
    end;
  end;
  finally; LeaveCriticalSection( GlobalCS ); end;
end;

function mon_dsql_row_fetch2 ( status:    pstatus_vector;
                               tr_handle: pisc_tr_handle;
                               st_handle: pisc_stmt_handle;
                               a:         word;
                               inparams:  PXSQLDA;
                               outparams: PXSQLDA
                               ):         isc_status; stdcall;
var
  ii: integer;
begin
  EnterCriticalSection( GlobalCS ); try
  for ii := 0 to Unit_MonitorList.Count - 1 do begin
    with TIB_Monitor(Unit_MonitorList.Items[ ii ]) do if Enabled then begin
      StartItem;
      AppendItem( 'EXECUTE2 SINGLETON' );
      AppendItem( IB_TR_HANDLE + ' = ' + IntToStr( Integer(tr_handle^)));
      AppendItem( IB_STMT_HANDLE + ' = ' + IntToStr( Integer(st_handle^)));
    end;
  end;
  finally; LeaveCriticalSection( GlobalCS ); end;
  Result := dll_dsql_execute2 ( status,
                                tr_handle,
                                st_handle,
                                a,
                                inparams,
                                outparams );
  EnterCriticalSection( GlobalCS ); try
  for ii := 0 to Unit_MonitorList.Count - 1 do begin
    with TIB_Monitor( Unit_MonitorList.Items[ ii ] ) do if Enabled then begin
      AppendItem( 'PARAMS = [ ' +
                  GetParamBuffers( inparams, ShowColOwner ) + ' ]' );
      AppendItem( 'FIELDS = [ ' +
                  GetParamBuffers( outparams, ShowColOwner ) + ' ]' );
      EndItem( Result );
    end;
  end;
  finally; LeaveCriticalSection( GlobalCS ); end;
end;

function mon_dsql_set_row_cursor_name ( status_vector: pstatus_vector;
                                        st_handle:     pisc_stmt_handle;
                                        cursor_name:   PChar;
                                        unusedType:    word
                                        ):             isc_status; stdcall;
var
  ii: integer;
begin
  EnterCriticalSection( GlobalCS ); try
  for ii := 0 to Unit_MonitorList.Count - 1 do begin
    with TIB_Monitor(Unit_MonitorList.Items[ ii ]) do if Enabled then begin
      StartItem;
      AppendItem( 'OPEN ROW CURSOR' );
      AppendItem( IB_STMT_HANDLE + ' = ' + IntToStr( Integer(st_handle^)));
      AppendItem( 'NAME = ' + cursor_name );
    end;
  end;
  finally; LeaveCriticalSection( GlobalCS ); end;
  Result := dll_dsql_set_cursor_name ( status_vector,
                                       st_handle,
                                       cursor_name,
                                       unusedType );
  EnterCriticalSection( GlobalCS ); try
  for ii := 0 to Unit_MonitorList.Count - 1 do begin
    with TIB_Monitor(Unit_MonitorList.Items[ ii ]) do if Enabled then begin
      EndItem( Result );
    end;
  end;
  finally; LeaveCriticalSection( GlobalCS ); end;
end;

function mon_dsql_free_row_statement ( status_vector: pstatus_vector;
                                       st_handle:     pisc_stmt_handle;
                                       option:        word
                                       ):             isc_status; stdcall;
var
  ii: integer;
begin
  EnterCriticalSection( GlobalCS ); try
  for ii := 0 to Unit_MonitorList.Count - 1 do begin
    with TIB_Monitor(Unit_MonitorList.Items[ ii ]) do if Enabled then begin
      StartItem;
      AppendItem( 'CLOSE ROW CURSOR' );
      AppendItem( IB_STMT_HANDLE + ' = ' + IntToStr( Integer(st_handle^)));
    end;
  end;
  finally; LeaveCriticalSection( GlobalCS ); end;
  Result := dll_dsql_free_statement ( status_vector, st_handle, option );
  EnterCriticalSection( GlobalCS ); try
  for ii := 0 to Unit_MonitorList.Count - 1 do begin
    with TIB_Monitor(Unit_MonitorList.Items[ ii ]) do if Enabled then begin
      EndItem( Result );
    end;
  end;
  finally; LeaveCriticalSection( GlobalCS ); end;
end;

{------------------------------------------------------------------------------}

function mon_interprete ( buffer: PChar;
                          status: ppstatus_vector
                          ):      isc_status; stdcall;
var
  ii: integer;
begin
  EnterCriticalSection( GlobalCS ); try
  for ii := 0 to Unit_MonitorList.Count - 1 do begin
    with TIB_Monitor(Unit_MonitorList.Items[ ii ]) do if Enabled then begin
    if MonitorGroups <> [] then begin
      StartItem;
      AppendItem( 'INTERPRET BUFFER = ' + StrPas( buffer ));
    end;
    end;
  end;
  finally; LeaveCriticalSection( GlobalCS ); end;
  Result := dll_interprete ( buffer, status );
  EnterCriticalSection( GlobalCS ); try
  for ii := 0 to Unit_MonitorList.Count - 1 do begin
    with TIB_Monitor(Unit_MonitorList.Items[ ii ]) do if Enabled then begin
    if MonitorGroups <> [] then begin
      if Result = 0 then begin
        EndItem( -1 );
      end else begin
        EndItem( Result );
      end;
    end;
    end;
  end;
  finally; LeaveCriticalSection( GlobalCS ); end;
end;

function mon_fb_interpret( buffer: PChar;
                           buflen: dword;
                           status: ppstatus_vector
                           ):      isc_status; stdcall;
var
  ii: integer;
begin
  EnterCriticalSection( GlobalCS ); try
  for ii := 0 to Unit_MonitorList.Count - 1 do begin
    with TIB_Monitor(Unit_MonitorList.Items[ ii ]) do if Enabled then begin
    if MonitorGroups <> [] then begin
      StartItem;
      AppendItem( 'INTERPRET BUFFER = ' + StrPas( buffer ));
    end;
    end;
  end;
  finally; LeaveCriticalSection( GlobalCS ); end;
  if Assigned( dll_fb_interpret ) then
    Result := dll_fb_interpret( buffer, buflen, status )
  else
    Result := dll_interprete( buffer, status );
  EnterCriticalSection( GlobalCS ); try
  for ii := 0 to Unit_MonitorList.Count - 1 do begin
    with TIB_Monitor(Unit_MonitorList.Items[ ii ]) do if Enabled then begin
    if MonitorGroups <> [] then begin
      if Result = 0 then begin
        EndItem( -1 );
      end else begin
        EndItem( Result );
      end;
    end;
    end;
  end;
  finally; LeaveCriticalSection( GlobalCS ); end;
end;

{------------------------------------------------------------------------------}
{
procedure mon_blob_default_desc (  a: pISC_BLOB_DESC;
                                   b: pchar;
                                   c: pchar ); stdcall;

var
  ii: integer;
begin
  dll_blob_default_desc( a, b, c );
  EnterCriticalSection( GlobalCS ); try
  for ii := 0 to Unit_MonitorList.Count - 1 do begin
    with TIB_Monitor(Unit_MonitorList.Items[ ii ]) do if Enabled then begin
    if ( mgBlob in MonitorGroups ) then begin
      StartItem;
      AppendItem( 'GET DEFAULT BLOB DESC' );
      EndItem( Result );
    end;
    end;
  end;
  finally; LeaveCriticalSection( GlobalCS ); end;
end;

function mon_blob_gen_bpb ( status: pstatus_vector;
    a:    pISC_BLOB_DESC;
    b:    pISC_BLOB_DESC;
    c:    short;
    d:    pchar;
    e:    pshort
                            ):      isc_status; stdcall;
var
  ii: integer;
begin
  EnterCriticalSection( GlobalCS ); try
  for ii := 0 to Unit_MonitorList.Count - 1 do begin
    with TIB_Monitor(Unit_MonitorList.Items[ ii ]) do if Enabled then begin
    if ( mgBlob in MonitorGroups ) then begin
      StartItem;
      AppendItem( 'GENERATE BLOB PARAMETER BUFFER' );
    end;
    end;
  end;
  finally; LeaveCriticalSection( GlobalCS ); end;
  Result := dll_blob_gen_bpb( status, a, b, c, d, e );
  EnterCriticalSection( GlobalCS ); try
  for ii := 0 to Unit_MonitorList.Count - 1 do begin
    with TIB_Monitor(Unit_MonitorList.Items[ ii ]) do if Enabled then begin
    if ( mgBlob in MonitorGroups ) then begin
      if Result <> 0 then
      AppendItem( IB_ERRCODE + ' = ' + IntToStr( Result ));
      EndItem( Result );
    end;
    end;
  end;
  finally; LeaveCriticalSection( GlobalCS ); end;
end;

function mon_blob_info ( status: pstatus_vector;
                         a:      pisc_blob_handle;
                         b:      short;
                         c:      pchar;
                         d:      short;
                         e:      pchar
                         ):      isc_status; stdcall;
var
  ii: integer;
begin
  EnterCriticalSection( GlobalCS ); try
  for ii := 0 to Unit_MonitorList.Count - 1 do begin
    with TIB_Monitor(Unit_MonitorList.Items[ ii ]) do if Enabled then begin
    if ( mgBlob in MonitorGroups ) then begin
      StartItem;
      AppendItem( 'GET BLOB INFO' );
    end;
    end;
  end;
  finally; LeaveCriticalSection( GlobalCS ); end;
  Result := dll_blob_info( status, a, b, c, d, e );
  EnterCriticalSection( GlobalCS ); try
  for ii := 0 to Unit_MonitorList.Count - 1 do begin
    with TIB_Monitor(Unit_MonitorList.Items[ ii ]) do if Enabled then begin
    if ( mgBlob in MonitorGroups ) then begin
      if Result <> 0 then
      AppendItem( IB_ERRCODE + ' = ' + IntToStr( Result ));
      EndItem( Result );
    end;
    end;
  end;
  finally; LeaveCriticalSection( GlobalCS ); end;
end;

function mon_blob_lookup_desc( status: pstatus_vector;
    a: pisc_db_handle;
    b: pisc_tr_handle;
    c: pchar;
    d: pchar;
    e: pISC_BLOB_DESC;
    f: pchar
                                    ): isc_status; stdcall ;
var
  ii: integer;
begin
  EnterCriticalSection( GlobalCS ); try
  for ii := 0 to Unit_MonitorList.Count - 1 do begin
    with TIB_Monitor(Unit_MonitorList.Items[ ii ]) do if Enabled then begin
    if ( mgBlob in MonitorGroups ) then begin
      StartItem;
      AppendItem( 'LOOKUP BLOB DESCRIPTION' );
    end;
    end;
  end;
  finally; LeaveCriticalSection( GlobalCS ); end;
  Result := dll_blob_lookup_desc(status, a, b, c, d, e, f);
  EnterCriticalSection( GlobalCS ); try
  for ii := 0 to Unit_MonitorList.Count - 1 do begin
    with TIB_Monitor(Unit_MonitorList.Items[ ii ]) do if Enabled then begin
    if ( mgBlob in MonitorGroups ) then begin
      if Result <> 0 then
      AppendItem( IB_ERRCODE + ' = ' + IntToStr( Result ));
      EndItem( Result );
    end;
    end;
  end;
  finally; LeaveCriticalSection( GlobalCS ); end;
end;

function mon_blob_set_desc( status: pstatus_vector;
                            a:      pchar;
                            b:      pchar;
                            c:      short;
                            d:      short;
                            e:      short;
                            f:      pISC_BLOB_DESC
                            ):      isc_status; stdcall;
var
  ii: integer;
begin
  EnterCriticalSection( GlobalCS ); try
  for ii := 0 to Unit_MonitorList.Count - 1 do begin
    with TIB_Monitor(Unit_MonitorList.Items[ ii ]) do if Enabled then begin
    if ( mgBlob in MonitorGroups ) then begin
      StartItem;
      AppendItem( 'SET BLOB DESCRIPTION' );
    end;
    end;
  end;
  finally; LeaveCriticalSection( GlobalCS ); end;
  Result := dll_blob_set_desc ( status, a, b, c, d, e, f );
  EnterCriticalSection( GlobalCS ); try
  for ii := 0 to Unit_MonitorList.Count - 1 do begin
    with TIB_Monitor(Unit_MonitorList.Items[ ii ]) do if Enabled then begin
    if ( mgBlob in MonitorGroups ) then begin
      if Result <> 0 then
      AppendItem( IB_ERRCODE + ' = ' + IntToStr( Result ));
      EndItem( Result );
    end;
    end;
  end;
  finally; LeaveCriticalSection( GlobalCS ); end;
end;

function mon_cancel_blob ( status:          pstatus_vector;
                           blob_handle:     pisc_blob_handle
                           ):               isc_status; stdcall;
var
  ii: integer;
begin
  EnterCriticalSection( GlobalCS ); try
  for ii := 0 to Unit_MonitorList.Count - 1 do begin
    with TIB_Monitor(Unit_MonitorList.Items[ ii ]) do if Enabled then begin
    if ( mgBlob in MonitorGroups ) then begin
      StartItem;
      AppendItem( 'CANCEL BLOB' );
    end;
    end;
  end;
  finally; LeaveCriticalSection( GlobalCS ); end;
  Result := dll_cancel_blob ( status, blob_handle );
  EnterCriticalSection( GlobalCS ); try
  for ii := 0 to Unit_MonitorList.Count - 1 do begin
    with TIB_Monitor(Unit_MonitorList.Items[ ii ]) do if Enabled then begin
    if ( mgBlob in MonitorGroups ) then begin
      if Result <> 0 then
      AppendItem( IB_ERRCODE + ' = ' + IntToStr( Result ));
      EndItem( Result );
    end;
    end;
  end;
  finally; LeaveCriticalSection( GlobalCS ); end;
end;
}
function mon_close_blob ( status:      pstatus_vector;
                          blob_handle: pisc_blob_handle
                          ):           isc_status; stdcall;
var
  ii: integer;
begin
  EnterCriticalSection( GlobalCS ); try
  for ii := 0 to Unit_MonitorList.Count - 1 do begin
    with TIB_Monitor(Unit_MonitorList.Items[ ii ]) do if Enabled then begin
    if ( mgBlob in MonitorGroups ) then begin
      StartItem;
      AppendItem( 'CLOSE BLOB HANDLE ' + IntToStr( Integer(blob_handle^)));
    end;
    end;
  end;
  finally; LeaveCriticalSection( GlobalCS ); end;
  Result := dll_close_blob ( status, blob_handle );
  EnterCriticalSection( GlobalCS ); try
  for ii := 0 to Unit_MonitorList.Count - 1 do begin
    with TIB_Monitor(Unit_MonitorList.Items[ ii ]) do if Enabled then begin
    if ( mgBlob in MonitorGroups ) then begin
      EndItem( Result );
    end;
    end;
  end;
  finally; LeaveCriticalSection( GlobalCS ); end;
end;

function mon_create_blob ( status: pstatus_vector;
                           a:      pisc_db_handle;
                           b:      pisc_tr_handle;
                           c:      pisc_blob_handle;
                           d:      pISC_QUAD
                           ):      isc_status; stdcall;
var
  ii: integer;
begin
  EnterCriticalSection( GlobalCS ); try
  for ii := 0 to Unit_MonitorList.Count - 1 do begin
    with TIB_Monitor(Unit_MonitorList.Items[ ii ]) do if Enabled then begin
    if ( mgBlob in MonitorGroups ) then begin
      StartItem;
    end;
    end;
  end;
  finally; LeaveCriticalSection( GlobalCS ); end;
  Result := dll_create_blob ( status, a, b, c, d );
  EnterCriticalSection( GlobalCS ); try
  for ii := 0 to Unit_MonitorList.Count - 1 do begin
    with TIB_Monitor(Unit_MonitorList.Items[ ii ]) do if Enabled then begin
    if ( mgBlob in MonitorGroups ) then begin
      AppendItem( 'CREATE BLOB HANDLE ' + IntToStr( Integer(c^)));
      AppendItem( 'BLOB ID ' + IntToStr( d.isc_quad_high ) + ', ' +
                                  IntToStr( d.isc_quad_low ));
      EndItem( Result );
    end;
    end;
  end;
  finally; LeaveCriticalSection( GlobalCS ); end;
end;

function mon_create_blob2 ( status: pstatus_vector;
                            a:	    pisc_db_handle;
                            b:	    pisc_tr_handle;
                            c:	    pisc_blob_handle;
                            d:	    pISC_QUAD;
                            e:	    short;
                            f:	    pchar
                            ):      isc_status; stdcall;
var
  ii: integer;
begin
  EnterCriticalSection( GlobalCS ); try
  for ii := 0 to Unit_MonitorList.Count - 1 do begin
    with TIB_Monitor(Unit_MonitorList.Items[ ii ]) do if Enabled then begin
    if ( mgBlob in MonitorGroups ) then begin
      StartItem;
    end;
    end;
  end;
  finally; LeaveCriticalSection( GlobalCS ); end;
  Result := dll_create_blob2 ( status, a, b, c, d, e, f );
  EnterCriticalSection( GlobalCS ); try
  for ii := 0 to Unit_MonitorList.Count - 1 do begin
    with TIB_Monitor(Unit_MonitorList.Items[ ii ]) do if Enabled then begin
    if ( mgBlob in MonitorGroups ) then begin
      AppendItem( 'CREATE 2 BLOB HANDLE ' + IntToStr( Integer(c^)));
      AppendItem( 'BLOB ID ' + IntToStr( d.isc_quad_high ) + ', ' +
                               IntToStr( d.isc_quad_low ));
      EndItem( Result );
    end;
    end;
  end;
  finally; LeaveCriticalSection( GlobalCS ); end;
end;

function mon_get_segment ( status: pstatus_vector;
                           a:      pisc_blob_handle;
                           b:      pword;
                           c:      word;
                           d:      pchar
                           ):      isc_status; stdcall;
var
  ii: integer;
begin
  EnterCriticalSection( GlobalCS ); try
  for ii := 0 to Unit_MonitorList.Count - 1 do begin
    with TIB_Monitor(Unit_MonitorList.Items[ ii ]) do if Enabled then begin
    if ( mgBlob in MonitorGroups ) then begin
      StartItem;
      AppendItem( 'GET SEGMENT BLOB HANDLE ' + IntToStr( Integer(a^)));
    end;
    end;
  end;
  finally; LeaveCriticalSection( GlobalCS ); end;
  Result := dll_get_segment( status, a, b, c, d );
  EnterCriticalSection( GlobalCS ); try
  for ii := 0 to Unit_MonitorList.Count - 1 do begin
    with TIB_Monitor(Unit_MonitorList.Items[ ii ]) do if Enabled then begin
    if ( mgBlob in MonitorGroups ) then begin
      AppendItem( 'SEGMENT SIZE = ' + IntToStr( b^ ));
      EndItem( Result );
    end;
    end;
  end;
  finally; LeaveCriticalSection( GlobalCS ); end;
end;

function mon_put_segment ( status: pstatus_vector;
                           a:      pisc_blob_handle;
                           b:      word;
                           c:      pchar
                           ):      isc_status; stdcall;
var
  ii: integer;
begin
  EnterCriticalSection( GlobalCS ); try
  for ii := 0 to Unit_MonitorList.Count - 1 do begin
    with TIB_Monitor(Unit_MonitorList.Items[ ii ]) do if Enabled then begin
    if ( mgBlob in MonitorGroups ) then begin
      StartItem;
    end;
    end;
  end;
  finally; LeaveCriticalSection( GlobalCS ); end;
  Result := dll_put_segment ( status, a, b, c );
  EnterCriticalSection( GlobalCS ); try
  for ii := 0 to Unit_MonitorList.Count - 1 do begin
    with TIB_Monitor(Unit_MonitorList.Items[ ii ]) do if Enabled then begin
    if ( mgBlob in MonitorGroups ) then begin
      AppendItem( 'PUT SEGMENT BLOB HANDLE ' + IntToStr( Integer(a^)));
      AppendItem( 'SEGMENT SIZE = ' + IntToStr( b ));
      EndItem( Result );
    end;
    end;
  end;
  finally; LeaveCriticalSection( GlobalCS ); end;
end;

function mon_open_blob ( status:  pstatus_vector;
                         a:       pisc_db_handle;
                         b:       pisc_tr_handle;
                         c:       pisc_blob_handle;
                         d:       pISC_QUAD
                         ):       isc_status; stdcall;
var
  ii: integer;
begin
  EnterCriticalSection( GlobalCS ); try
  for ii := 0 to Unit_MonitorList.Count - 1 do begin
    with TIB_Monitor(Unit_MonitorList.Items[ ii ]) do if Enabled then begin
    if ( mgBlob in MonitorGroups ) then begin
      StartItem;
    end;
    end;
  end;
  finally; LeaveCriticalSection( GlobalCS ); end;
  Result := dll_open_blob ( status, a, b, c, d );
  EnterCriticalSection( GlobalCS ); try
  for ii := 0 to Unit_MonitorList.Count - 1 do begin
    with TIB_Monitor(Unit_MonitorList.Items[ ii ]) do if Enabled then begin
    if ( mgBlob in MonitorGroups ) then begin
      AppendItem( 'OPEN BLOB HANDLE ' + IntToStr( integer(c^)));
      AppendItem( 'BLOB ID ' + IntToStr( d.isc_quad_high ) + ', ' +
                               IntToStr( d.isc_quad_low ));
      EndItem( Result );
    end;
    end;
  end;
  finally; LeaveCriticalSection( GlobalCS ); end;
end;

function mon_open_blob2 ( status: pstatus_vector;
                          a:      pisc_db_handle;
                          b:      pisc_tr_handle;
                          c:      pisc_blob_handle;
                          d:      pISC_QUAD;
                          e:      short;
                          g:      pchar
                          ):      isc_status; stdcall;
var
  ii: integer;
begin
  EnterCriticalSection( GlobalCS ); try
  for ii := 0 to Unit_MonitorList.Count - 1 do begin
    with TIB_Monitor(Unit_MonitorList.Items[ ii ]) do if Enabled then begin
    if ( mgBlob in MonitorGroups ) then begin
      StartItem;
      AppendItem( 'OPEN BLOB 2 HANDLE ' + IntToStr( Integer(c^)));
      AppendItem( 'BLOB ID ' + IntToStr( d.isc_quad_high ) + ', ' +
                                  IntToStr( d.isc_quad_low ));
    end;
    end;
  end;
  finally; LeaveCriticalSection( GlobalCS ); end;
  Result := dll_open_blob2 ( status, a, b, c, d, e, g );
  EnterCriticalSection( GlobalCS ); try
  for ii := 0 to Unit_MonitorList.Count - 1 do begin
    with TIB_Monitor(Unit_MonitorList.Items[ ii ]) do if Enabled then begin
    if ( mgBlob in MonitorGroups ) then begin
      EndItem( Result );
    end;
    end;
  end;
  finally; LeaveCriticalSection( GlobalCS ); end;
end;

{------------------------------------------------------------------------------}

function mon_array_get_slice ( status:  pstatus_vector;
                               a:       pisc_db_handle;
                               b:       pisc_tr_handle;
                               c:       PISC_QUAD;
                               d:       PISC_ARRAY_DESC;
                               e:       pointer;
                               f:       PISC_LONG
                               ):       isc_status; stdcall;
var
  ii: integer;
begin
  EnterCriticalSection( GlobalCS ); try
  for ii := 0 to Unit_MonitorList.Count - 1 do begin
    with TIB_Monitor(Unit_MonitorList.Items[ ii ]) do if Enabled then begin
    if ( mgArray in MonitorGroups ) then begin
      StartItem;
      AppendItem( 'GET SLICE' );
    end;
    end;
  end;
  finally; LeaveCriticalSection( GlobalCS ); end;
  Result := dll_array_get_slice ( status,a, b, c, d, e, f );
  EnterCriticalSection( GlobalCS ); try
  for ii := 0 to Unit_MonitorList.Count - 1 do begin
    with TIB_Monitor(Unit_MonitorList.Items[ ii ]) do if Enabled then begin
    if ( mgArray in MonitorGroups ) then begin
      EndItem( Result );
    end;
    end;
  end;
  finally; LeaveCriticalSection( GlobalCS ); end;
end;

function mon_array_put_slice ( status:  pstatus_vector;
                               a:       pisc_db_handle;
                               b:       pisc_tr_handle;
                               c:       PISC_QUAD;
                               d:       PISC_ARRAY_DESC;
                               e:       pointer;
                               f:       PISC_LONG
                               ):       isc_status; stdcall;
var
  ii: integer;
begin
  EnterCriticalSection( GlobalCS ); try
  for ii := 0 to Unit_MonitorList.Count - 1 do begin
    with TIB_Monitor(Unit_MonitorList.Items[ ii ]) do if Enabled then begin
    if ( mgArray in MonitorGroups ) then begin
      StartItem;
      AppendItem( 'PUT SLICE' );
    end;
    end;
  end;
  finally; LeaveCriticalSection( GlobalCS ); end;
  Result := dll_array_put_slice ( status, a, b, c, d, e, f );
  EnterCriticalSection( GlobalCS ); try
  for ii := 0 to Unit_MonitorList.Count - 1 do begin
    with TIB_Monitor(Unit_MonitorList.Items[ ii ]) do if Enabled then begin
    if ( mgArray in MonitorGroups ) then begin
      EndItem( Result );
    end;
    end;
  end;
  finally; LeaveCriticalSection( GlobalCS ); end;
end;

{
  Tisc_array_lookup_bounds = function( status:          pstatus_vector;
                                       a: pisc_db_handle;
                                       b: pisc_tr_handle;
                                       c: pchar;
                                       d: pchar;
                                       e: pISC_ARRAY_DESC
                                       ):   isc_status; stdcall;

  Tisc_array_lookup_desc = function( status:          pstatus_vector;
                                     a:               pisc_db_handle;
                                     b:               pisc_tr_handle;
                                     c:               pchar;
                                     d:               pchar;
                                     e:               pISC_ARRAY_DESC
                                     ):               isc_status; stdcall;

  Tisc_array_set_desc = function( status:       pstatus_vector;
                                  a:            pchar;
                                  b:            pchar;
                                  c:            pshort;
                                  d:            pshort;
                                  e:            pshort;
                                  f:            pISC_ARRAY_DESC
                                  ):            isc_status; stdcall;

  Tisc_array_gen_sdl = function( status:      pstatus_vector;
                                 array_desc:  pISC_ARRAY_DESC;
                                 array_1:     pshort;
                                 array_buf:   pchar;
                                 array_2:     pshort
                                 ):           isc_status; stdcall;

  Tisc_dsql_fetch2 = function ( status_vector: pstatus_vector;
                                st_handle:     pisc_stmt_handle;
                                dialect:       word;
                                params:        PXSQLDA;
                                fetchtype:     smallint;
                                fetchno:       longint
                                ):             isc_status; stdcall;

  Tisc_get_slice = function( status: pstatus_vector;
                             a:      pisc_db_handle;
                             b:      pisc_tr_handle;
                             c:      pISC_QUAD;
                             d:      short;
                             e:      pchar;
                             f:      short;
                             g:      pISC_LONG;
                             h:      ISC_LONG;
                             i:      pointer;
                             j:      PISC_LONG
                             ):      isc_status; stdcall;

  Tisc_put_slice = function( status:          pstatus_vector;
                             a:               pisc_db_handle;
                             b:               pisc_tr_handle;
                             c:               pISC_QUAD;
                             d:               short;
                             e:               pchar;
                             f:               short;
                             g:               pISC_LONG;
                             h:               ISC_LONG;
                             i:               pointer
                             ):               isc_status; stdcall;
}

procedure PutInRowMonitorHooks;
begin
  isc_dsql_fetch :=               mon_dsql_fetch;
  isc_dsql_row_fetch :=           mon_dsql_row_fetch;
  isc_dsql_row_fetch2 :=          mon_dsql_row_fetch2;
  isc_dsql_set_row_cursor_name := mon_dsql_set_row_cursor_name;
  isc_dsql_free_row_statement :=  mon_dsql_free_row_statement;
  FRowMonitorHooksIn := true;
end;

procedure PutInMonitorHooks;
begin
  // Make sure the library has been acquired before we do anything
  if not Assigned( isc_attach_database ) then
    RevertToOriginalHooks; // acquires hooks if necessary
  isc_attach_database :=          mon_attach_database;
  isc_detach_database :=          mon_detach_database;
  isc_drop_database :=            mon_drop_database;
  isc_database_info :=            mon_database_info;
  isc_commit_retaining :=         mon_commit_retaining;
  isc_commit_transaction :=       mon_commit_transaction;
  isc_rollback_transaction :=     mon_rollback_transaction;
  isc_rollback_retaining :=       mon_rollback_retaining;
  isc_start_multiple :=           mon_start_multiple;
  isc_dsql_prepare :=             mon_dsql_prepare;
  isc_dsql_execute :=             mon_dsql_execute;
  isc_dsql_execute2 :=            mon_dsql_execute2;
  isc_dsql_execute_immediate :=   mon_dsql_execute_immediate;
  isc_dsql_exec_immed2 :=         mon_dsql_exec_immed2;
  isc_dsql_set_cursor_name :=     mon_dsql_set_cursor_name;
  isc_dsql_free_statement :=      mon_dsql_free_statement;
  isc_dsql_allocate_statement :=  mon_dsql_allocate_statement;
  isc_dsql_alloc_statement2 :=    mon_dsql_alloc_statement2;
  isc_dsql_describe :=            mon_dsql_describe;
  isc_dsql_describe_bind :=       mon_dsql_describe_bind;
  isc_dsql_sql_info :=            mon_dsql_sql_info;
  isc_interprete :=               mon_interprete;
  fb_interpret :=                 mon_fb_interpret;
  isc_close_blob :=               mon_close_blob;
  isc_create_blob :=              mon_create_blob;
  isc_create_blob2 :=             mon_create_blob2;
  isc_open_blob :=                mon_open_blob;
  isc_open_blob2 :=               mon_open_blob2;
  isc_get_segment :=              mon_get_segment;
  isc_put_segment :=              mon_put_segment;
  isc_array_get_slice :=          mon_array_get_slice;
  isc_array_put_slice :=          mon_array_put_slice;
{
  isc_cancel_blob :=              mon_cancel_blob;
  isc_blob_default_desc :=        mon_blob_default_desc;
  isc_blob_gen_bpb :=             mon_blob_gen_bpb;
  isc_blob_info :=                mon_blob_info;
  isc_blob_lookup_desc :=         mon_blob_lookup_desc;
  isc_blob_set_desc :=            mon_blob_set_desc;
}
  monitor_client_trace_method :=  mon_ClientTrace;
  monitor_profile_method :=       mon_Profile;

  FMonitorHooksIn := true;
end;

initialization
  ReserveSessionHookRef;
  EnterCriticalSection( GlobalCS );
  try
    Unit_MonitorList := TList.Create;
  finally;
    LeaveCriticalSection( GlobalCS );
  end;

finalization
  EnterCriticalSection( GlobalCS );
  try
    if Assigned( Unit_MonitorList ) and ( Unit_MonitorList.Count = 0 ) then
    begin
      Unit_MonitorList.Free;
      Unit_MonitorList := nil;
    end;
  finally;
    LeaveCriticalSection( GlobalCS );
  end;
  ReleaseSessionHookRef;

end.

