ViewVC Help
View File | Revision Log | Show Annotations | Download File | View Changeset | Root Listing
root/public/ibx/trunk/runtime/nongui/IBSQLMonitor.pas
Revision: 215
Committed: Thu Mar 15 16:25:03 2018 UTC (6 years, 8 months ago) by tony
Content type: text/x-pascal
File size: 34123 byte(s)
Log Message:
Fixes Merged

File Contents

# User Rev Content
1 tony 209 {************************************************************************}
2     { }
3     { Borland Delphi Visual Component Library }
4     { InterBase Express core components }
5     { }
6     { Copyright (c) 1998-2000 Inprise Corporation }
7     { }
8     { InterBase Express is based in part on the product }
9     { Free IB Components, written by Gregory H. Deatz for }
10     { Hoagland, Longo, Moran, Dunst & Doukas Company. }
11     { Free IB Components is used under license. }
12     { }
13     { The contents of this file are subject to the InterBase }
14     { Public License Version 1.0 (the "License"); you may not }
15     { use this file except in compliance with the License. You }
16     { may obtain a copy of the License at http://www.Inprise.com/IPL.html }
17     { Software distributed under the License is distributed on }
18     { an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either }
19     { express or implied. See the License for the specific language }
20     { governing rights and limitations under the License. }
21     { The Original Code was created by InterBase Software Corporation }
22     { and its successors. }
23     { Portions created by Inprise Corporation are Copyright (C) Inprise }
24     { Corporation. All Rights Reserved. }
25     { Contributor(s): Jeff Overcash }
26     { }
27     { IBX For Lazarus (Firebird Express) }
28     { Contributor: Tony Whyman, MWA Software http://www.mwasoftware.co.uk }
29     { Portions created by MWA Software are copyright McCallum Whyman }
30     { Associates Ltd 2011 - 2018 }
31     { }
32     {************************************************************************}
33    
34     {
35     This unit has been significantly revised for the Lazarus port. Specially,
36     there was a need to re-organise the code to isolate the Windows specific
37     IPC and to introduce SV5 IPC as an alternative for Linux and other platforms.
38     }
39    
40     unit IBSQLMonitor;
41    
42     {$Mode Delphi}
43    
44     {$codepage UTF8}
45    
46     interface
47    
48     uses
49     IB, IBUtils, IBSQL, IBCustomDataSet, IBDatabase, DB,
50     IBTypes ,SysUtils, Classes,
51     {$IFDEF WINDOWS }
52     Windows
53     {$ENDIF}
54 tony 215 {$IFDEF UNIX}
55     cthreads, unix
56     {$ENDIF}
57 tony 209 ;
58    
59     {Note that the original inter-thread communication between the Reader Thread and
60     the ISQL Monitor used the Windows PostMessage interface. This is currently not
61     useable under the FPC RTL as AllocateHWnd is not functional. It has been replaced
62     by the use of the Synchronize method.}
63    
64     {$IFDEF WINDOWS}
65     {$DEFINE USE_WINDOWS_IPC}
66     {$ENDIF}
67    
68     {$IFDEF UNIX}
69     {$DEFINE USE_SV5_IPC}
70     {$ENDIF}
71    
72     {$IFDEF LINUX}
73     {$DEFINE HAS_SEMTIMEDOP}
74     {$ENDIF}
75    
76     type
77     TIBCustomSQLMonitor = class;
78    
79     TSQLEvent = procedure(EventText: String; EventTime : TDateTime) of object;
80    
81     { TIBCustomSQLMonitor }
82    
83     TIBCustomSQLMonitor = class(TComponent)
84     private
85     FOnSQLEvent: TSQLEvent;
86     FTraceFlags: TTraceFlags;
87     FEnabled: Boolean;
88     procedure SetEnabled(const Value: Boolean);
89     protected
90     procedure ReleaseObject; {Called from Writer Thread}
91     procedure ReceiveMessage(Msg: TObject); {Called from Reader Thread}
92     property OnSQL: TSQLEvent read FOnSQLEvent write FOnSQLEvent;
93     property TraceFlags: TTraceFlags read FTraceFlags write FTraceFlags;
94     property Enabled : Boolean read FEnabled write SetEnabled default true;
95     public
96     constructor Create(AOwner: TComponent); override;
97     destructor Destroy; override;
98     procedure Release;
99     end;
100    
101     { TIBSQLMonitor }
102    
103     TIBSQLMonitor = class(TIBCustomSQLMonitor)
104     published
105     property OnSQL;
106     property TraceFlags;
107     property Enabled;
108     end;
109    
110     TIBMonitoredService = class(TComponent)
111     private
112     FTraceFlags: TTraceFlags;
113     protected
114     property TraceFlags: TTraceFlags read FTraceFlags write FTraceFlags;
115     end;
116    
117     { TIBXMonitoredService }
118    
119     TIBXMonitoredService = class(TComponent)
120     private
121     FTraceFlags: TTraceFlags;
122     protected
123     property TraceFlags: TTraceFlags read FTraceFlags write FTraceFlags;
124     end;
125    
126     TIBXMonitoredConnection = class(TCustomConnection)
127     private
128     FTraceFlags: TTraceFlags;
129     protected
130     property TraceFlags: TTraceFlags read FTraceFlags write FTraceFlags;
131     end;
132    
133     IIBSQLMonitorHook = interface
134     ['{CF65434C-9B75-4298-BA7E-E6B85B3C769D}']
135     procedure RegisterMonitor(SQLMonitor : TIBCustomSQLMonitor);
136     procedure UnregisterMonitor(SQLMonitor : TIBCustomSQLMonitor);
137     procedure ReleaseMonitor(Arg : TIBCustomSQLMonitor);
138     procedure SQLPrepare(qry: TIBSQL);
139     procedure SQLExecute(qry: TIBSQL);
140     procedure SQLFetch(qry: TIBSQL);
141     procedure DBConnect(db: TIBDatabase);
142     procedure DBDisconnect(db: TIBDatabase);
143     procedure TRStart(tr: TIBTransaction);
144     procedure TRCommit(tr: TIBTransaction);
145     procedure TRCommitRetaining(tr: TIBTransaction);
146     procedure TRRollback(tr: TIBTransaction);
147     procedure TRRollbackRetaining(tr: TIBTransaction);
148     procedure ServiceAttach(service: TIBMonitoredService); overload;
149     procedure ServiceDetach(service: TIBMonitoredService); overload;
150     procedure ServiceQuery(service: TIBMonitoredService); overload;
151     procedure ServiceStart(service: TIBMonitoredService); overload;
152     procedure ServiceAttach(service: TIBXMonitoredConnection); overload;
153     procedure ServiceDetach(service: TIBXMonitoredConnection); overload;
154     procedure ServiceQuery(service: TIBXMonitoredService); overload;
155     procedure ServiceStart(service: TIBXMonitoredService); overload;
156     procedure SendMisc(Msg : String);
157     function GetTraceFlags : TTraceFlags;
158     function GetMonitorCount : Integer;
159     procedure SetTraceFlags(const Value : TTraceFlags);
160     function GetEnabled : boolean;
161     procedure SetEnabled(const Value : Boolean);
162     property TraceFlags: TTraceFlags read GetTraceFlags write SetTraceFlags;
163     property Enabled : Boolean read GetEnabled write SetEnabled;
164     end;
165    
166    
167     function MonitorHook: IIBSQLMonitorHook;
168     procedure EnableMonitoring;
169     procedure DisableMonitoring;
170     function MonitoringEnabled: Boolean;
171    
172     implementation
173    
174     uses
175     contnrs, syncobjs, CustApp, FBMessages
176     {$IFDEF USE_SV5_IPC}
177     ,ipc, Errors, baseunix
178     {$ENDIF};
179    
180    
181     const
182     cMonitorHookSize = 1024;
183     cMsgWaitTime = 1000;
184     cWriteMessageAvailable = 'WriterMsgQueue';
185    
186     type
187     { There are two possible objects. One is a trace message object.
188     This object holds the flag of the trace type plus the message.
189     The second object is a Release object. It holds the handle that
190     the CM_RELEASE message is to be queued to. }
191    
192     { TTraceObject }
193    
194     TTraceObject = Class(TObject)
195     FDataType : TTraceFlag;
196     FMsg : String;
197     FTimeStamp : TDateTime;
198     public
199     constructor Create(Msg : String; DataType : TTraceFlag); overload;
200     constructor Create(obj : TTraceObject); overload;
201     constructor Create(obj : TTraceObject; MsgOffset, MsgLen: integer); overload;
202     end;
203    
204     { TReleaseObject }
205    
206     TReleaseObject = Class(TObject)
207     FMonitor : TIBCustomSQLMonitor;
208     public
209     constructor Create(Monitor : TIBCustomSQLMonitor);
210     end;
211    
212     {$IFDEF USE_SV5_IPC}
213     {$I sv5ipc.inc}
214     {$ENDIF}
215     {$IFDEF USE_WINDOWS_IPC}
216     {$I winipc.inc}
217     {$ENDIF}
218    
219     type
220    
221     { TIBSQLMonitorHook }
222    
223     TIBSQLMonitorHook = class(TInterfacedObject, IIBSQLMonitorHook)
224     private
225     FGlobalInterface: TGlobalInterface;
226     FTraceFlags: TTraceFlags;
227     FEnabled: Boolean;
228     protected
229     procedure WriteSQLData(Text: String; DataType: TTraceFlag);
230     public
231     constructor Create;
232     destructor Destroy; override;
233     procedure RegisterMonitor(SQLMonitor : TIBCustomSQLMonitor);
234     procedure UnregisterMonitor(SQLMonitor : TIBCustomSQLMonitor);
235     procedure ReleaseMonitor(Arg : TIBCustomSQLMonitor);
236     procedure SQLPrepare(qry: TIBSQL); virtual;
237     procedure SQLExecute(qry: TIBSQL); virtual;
238     procedure SQLFetch(qry: TIBSQL); virtual;
239     procedure DBConnect(db: TIBDatabase); virtual;
240     procedure DBDisconnect(db: TIBDatabase); virtual;
241     procedure TRStart(tr: TIBTransaction); virtual;
242     procedure TRCommit(tr: TIBTransaction); virtual;
243     procedure TRCommitRetaining(tr: TIBTransaction); virtual;
244     procedure TRRollback(tr: TIBTransaction); virtual;
245     procedure TRRollbackRetaining(tr: TIBTransaction); virtual;
246     procedure ServiceAttach(service: TIBMonitoredService); virtual; overload;
247     procedure ServiceDetach(service: TIBMonitoredService); virtual; overload;
248     procedure ServiceQuery(service: TIBMonitoredService); virtual; overload;
249     procedure ServiceStart(service: TIBMonitoredService); virtual; overload;
250     procedure ServiceAttach(service: TIBXMonitoredConnection); virtual; overload;
251     procedure ServiceDetach(service: TIBXMonitoredConnection); virtual; overload;
252     procedure ServiceQuery(service: TIBXMonitoredService); virtual; overload;
253     procedure ServiceStart(service: TIBXMonitoredService); virtual; overload;
254     procedure SendMisc(Msg : String);
255     function GetEnabled: Boolean;
256     function GetTraceFlags: TTraceFlags;
257     function GetMonitorCount : Integer;
258     procedure SetEnabled(const Value: Boolean);
259     procedure SetTraceFlags(const Value: TTraceFlags);
260     procedure ForceRelease;
261     property TraceFlags: TTraceFlags read GetTraceFlags write SetTraceFlags;
262     property Enabled : Boolean read GetEnabled write SetEnabled default true;
263     end;
264    
265     { TWriterThread }
266    
267     TWriterThread = class(TThread)
268     private
269     { Private declarations }
270     FGlobalInterface: TGlobalInterface;
271     FMsgs : TObjectList;
272     FCriticalSection: TCriticalSection;
273     FMsgAvailable: TEventObject;
274     procedure RemoveFromList;
275     procedure PostRelease;
276     public
277     procedure ReleaseMonitor(Arg : TIBCustomSQLMonitor);
278     protected
279     procedure BeginWrite;
280     procedure EndWrite;
281     procedure Execute; override;
282     procedure WriteToBuffer;
283     public
284     constructor Create(GlobalInterface: TGlobalInterface);
285     destructor Destroy; override;
286     procedure WriteSQLData(Msg : String; DataType : TTraceFlag);
287     end;
288    
289     { TReaderThread }
290    
291     TReaderThread = class(TThread)
292     private
293     { Private declarations }
294     st : TTraceObject;
295     FMonitors : TObjectList;
296     FGlobalInterface: TGlobalInterface;
297     FCriticalSection: TCriticalSection;
298     procedure AlertMonitors;
299     protected
300     procedure BeginRead;
301     procedure EndRead;
302     procedure ReadSQLData;
303     procedure Execute; override;
304     public
305     constructor Create(GlobalInterface: TGlobalInterface);
306     destructor Destroy; override;
307     procedure AddMonitor(Arg : TIBCustomSQLMonitor);
308     procedure RemoveMonitor(Arg : TIBCustomSQLMonitor);
309     end;
310    
311    
312     var
313     FWriterThread : TWriterThread;
314     FReaderThread : TReaderThread;
315     _MonitorHook: TIBSQLMonitorHook;
316     bDone: Boolean;
317     CS : TCriticalSection;
318    
319     const
320     ApplicationTitle: string = 'Unknown';
321    
322     { TIBCustomSQLMonitor }
323    
324     constructor TIBCustomSQLMonitor.Create(AOwner: TComponent);
325     var aParent: TComponent;
326     begin
327     inherited Create(AOwner);
328     FTraceFlags := [tfqPrepare .. tfMisc];
329     if not (csDesigning in ComponentState) then
330     begin
331     aParent := AOwner;
332     while aParent <> nil do
333     begin
334     if aParent is TCustomApplication then
335     begin
336     ApplicationTitle := TCustomApplication(aParent).Title;
337     break;
338     end;
339     aParent := aParent.Owner;
340     end;
341     MonitorHook.RegisterMonitor(self);
342     end;
343     FEnabled := true;
344     end;
345    
346     destructor TIBCustomSQLMonitor.Destroy;
347     begin
348     if not (csDesigning in ComponentState) then
349     begin
350     if FEnabled and assigned(_MonitorHook) then
351     MonitorHook.UnregisterMonitor(self);
352     end;
353     inherited Destroy;
354     end;
355    
356     procedure TIBCustomSQLMonitor.Release;
357     begin
358     MonitorHook.ReleaseMonitor(self);
359     end;
360    
361     procedure TIBCustomSQLMonitor.ReleaseObject;
362     begin
363     Free
364     end;
365    
366     procedure TIBCustomSQLMonitor.ReceiveMessage(Msg: TObject);
367     var
368     st: TTraceObject;
369     begin
370     st := (Msg as TTraceObject);
371     if (Assigned(FOnSQLEvent)) and
372     (st.FDataType in FTraceFlags) then
373     FOnSQLEvent(st.FMsg, st.FTimeStamp);
374     st.Free;
375     end;
376    
377     procedure TIBCustomSQLMonitor.SetEnabled(const Value: Boolean);
378     begin
379     if Value <> FEnabled then
380     begin
381     FEnabled := Value;
382     if not (csDesigning in ComponentState) then
383     if FEnabled then
384     Monitorhook.RegisterMonitor(self)
385     else
386     MonitorHook.UnregisterMonitor(self);
387     end;
388     end;
389    
390     { TIBSQLMonitorHook }
391    
392     constructor TIBSQLMonitorHook.Create;
393     begin
394     inherited Create;
395     FTraceFlags := [tfQPrepare..tfMisc];
396     FEnabled := false;
397     end;
398    
399     destructor TIBSQLMonitorHook.Destroy;
400     begin
401     if assigned(FGlobalInterface) then FGlobalInterface.Free;
402     inherited Destroy;
403     end;
404    
405     procedure TIBSQLMonitorHook.DBConnect(db: TIBDatabase);
406     var
407     st : String;
408     begin
409     if FEnabled then
410     begin
411     if not (tfConnect in FTraceFlags * db.TraceFlags) then
412     Exit;
413     st := db.Name + ': [Connect]'; {do not localize}
414     WriteSQLData(st, tfConnect);
415     end;
416     end;
417    
418     procedure TIBSQLMonitorHook.DBDisconnect(db: TIBDatabase);
419     var
420     st: String;
421     begin
422     if (Self = nil) then exit;
423     if FEnabled then
424     begin
425     if not (tfConnect in FTraceFlags * db.TraceFlags) then
426     Exit;
427     st := db.Name + ': [Disconnect]'; {do not localize}
428     WriteSQLData(st, tfConnect);
429     end;
430     end;
431    
432     function TIBSQLMonitorHook.GetEnabled: Boolean;
433     begin
434     Result := FEnabled;
435     end;
436    
437     function TIBSQLMonitorHook.GetMonitorCount: Integer;
438     begin
439     Result := FGlobalInterface.MonitorCount
440     end;
441    
442     function TIBSQLMonitorHook.GetTraceFlags: TTraceFlags;
443     begin
444     Result := FTraceFlags;
445     end;
446    
447     procedure TIBSQLMonitorHook.RegisterMonitor(SQLMonitor: TIBCustomSQLMonitor);
448     begin
449     {$IFDEF DEBUG}writeln('Register Monitor');{$ENDIF}
450     if not assigned(FGlobalInterface) then
451     FGlobalInterface := TGlobalInterface.Create;
452     if not Assigned(FReaderThread) then
453     FReaderThread := TReaderThread.Create(FGlobalInterface);
454     FReaderThread.AddMonitor(SQLMonitor);
455     end;
456    
457     procedure TIBSQLMonitorHook.ReleaseMonitor(Arg: TIBCustomSQLMonitor);
458     begin
459     FWriterThread.ReleaseMonitor(Arg);
460     end;
461    
462     procedure TIBSQLMonitorHook.SendMisc(Msg: String);
463     begin
464     if FEnabled then
465     begin
466     WriteSQLData(Msg, tfMisc);
467     end;
468     end;
469    
470     procedure TIBSQLMonitorHook.ServiceAttach(service: TIBMonitoredService);
471     var
472     st: String;
473     begin
474     if FEnabled then
475     begin
476     if not (tfService in (FTraceFlags * service.TraceFlags)) then
477     Exit;
478     st := service.Name + ': [Attach]'; {do not localize}
479     WriteSQLData(st, tfService);
480     end;
481     end;
482    
483     procedure TIBSQLMonitorHook.ServiceDetach(service: TIBMonitoredService);
484     var
485     st: String;
486     begin
487     if FEnabled then
488     begin
489     if not (tfService in (FTraceFlags * service.TraceFlags)) then
490     Exit;
491     st := service.Name + ': [Detach]'; {do not localize}
492     WriteSQLData(st, tfService);
493     end;
494     end;
495    
496     procedure TIBSQLMonitorHook.ServiceQuery(service: TIBMonitoredService);
497     var
498     st: String;
499     begin
500     if FEnabled then
501     begin
502     if not (tfService in (FTraceFlags * service.TraceFlags)) then
503     Exit;
504     st := service.Name + ': [Query]'; {do not localize}
505     WriteSQLData(st, tfService);
506     end;
507     end;
508    
509     procedure TIBSQLMonitorHook.ServiceStart(service: TIBMonitoredService);
510     var
511     st: String;
512     begin
513     if FEnabled then
514     begin
515     if not (tfService in (FTraceFlags * service.TraceFlags)) then
516     Exit;
517     st := service.Name + ': [Start]'; {do not localize}
518     WriteSQLData(st, tfService);
519     end;
520     end;
521    
522     procedure TIBSQLMonitorHook.ServiceAttach(service: TIBXMonitoredConnection);
523     var
524     st: String;
525     begin
526     if FEnabled then
527     begin
528     if not (tfService in (FTraceFlags * service.TraceFlags)) then
529     Exit;
530     st := service.Name + ': [Attach]'; {do not localize}
531     WriteSQLData(st, tfService);
532     end;
533     end;
534    
535     procedure TIBSQLMonitorHook.ServiceDetach(service: TIBXMonitoredConnection);
536     var
537     st: String;
538     begin
539     if FEnabled then
540     begin
541     if not (tfService in (FTraceFlags * service.TraceFlags)) then
542     Exit;
543     st := service.Name + ': [Detach]'; {do not localize}
544     WriteSQLData(st, tfService);
545     end;
546     end;
547    
548     procedure TIBSQLMonitorHook.ServiceQuery(service: TIBXMonitoredService);
549     var
550     st: String;
551     begin
552     if FEnabled then
553     begin
554     if not (tfService in (FTraceFlags * service.TraceFlags)) then
555     Exit;
556     st := service.Name + ': [Query]'; {do not localize}
557     WriteSQLData(st, tfService);
558     end;
559     end;
560    
561     procedure TIBSQLMonitorHook.ServiceStart(service: TIBXMonitoredService);
562     var
563     st: String;
564     begin
565     if FEnabled then
566     begin
567     if not (tfService in (FTraceFlags * service.TraceFlags)) then
568     Exit;
569     st := service.Name + ': [Start]'; {do not localize}
570     WriteSQLData(st, tfService);
571     end;
572     end;
573    
574     procedure TIBSQLMonitorHook.SetEnabled(const Value: Boolean);
575     begin
576     if FEnabled <> Value then
577     FEnabled := Value;
578     if (not FEnabled) and (Assigned(FWriterThread)) then
579     begin
580     FWriterThread.Terminate;
581     FWriterThread.WaitFor;
582     FreeAndNil(FWriterThread);
583     end;
584     end;
585    
586     procedure TIBSQLMonitorHook.SetTraceFlags(const Value: TTraceFlags);
587     begin
588     FTraceFlags := Value
589     end;
590    
591     procedure TIBSQLMonitorHook.ForceRelease;
592     begin
593     if Assigned(FReaderThread) then
594     begin
595     FReaderThread.Terminate;
596     if not Assigned(FWriterThread) then
597     FWriterThread := TWriterThread.Create(FGlobalInterface);
598     FWriterThread.WriteSQLData(' ', tfMisc);
599     end;
600     end;
601    
602     procedure TIBSQLMonitorHook.SQLExecute(qry: TIBSQL);
603     var
604     st: String;
605     i: Integer;
606     begin
607     if FEnabled then
608     begin
609     if not ((tfQExecute in (FTraceFlags * qry.Database.TraceFlags)) or
610     (tfStmt in (FTraceFlags * qry.Database.TraceFlags)) ) then
611     Exit;
612     if qry.Owner is TIBCustomDataSet then
613     st := TIBCustomDataSet(qry.Owner).Name
614     else
615     st := qry.Name;
616     st := st + ': [Execute] ' + qry.SQL.Text; {do not localize}
617     if qry.Params.GetCount > 0 then begin
618     for i := 0 to qry.Params.GetCount - 1 do begin
619     st := st + CRLF + ' ' + qry.Params[i].Name + ' = ';
620     try
621     if qry.Params[i].IsNull then
622     st := st + '<NULL>'; {do not localize}
623     st := st + qry.Params[i].AsString;
624     except
625     st := st + '<' + SCantPrintValue + '>';
626     end;
627     end;
628     end;
629     WriteSQLData(st, tfQExecute);
630     end;
631     end;
632    
633     procedure TIBSQLMonitorHook.SQLFetch(qry: TIBSQL);
634     var
635     st: String;
636     begin
637     if FEnabled then
638     begin
639     if not ((tfQFetch in (FTraceFlags * qry.Database.TraceFlags)) or
640     (tfStmt in (FTraceFlags * qry.Database.TraceFlags))) then
641     Exit;
642     if qry.Owner is TIBCustomDataSet then
643     st := TIBCustomDataSet(qry.Owner).Name
644     else
645     st := qry.Name;
646     st := st + ': [Fetch] ' + qry.SQL.Text; {do not localize}
647     if (qry.EOF) then
648     st := st + CRLF + ' ' + SEOFReached;
649     WriteSQLData(st, tfQFetch);
650     end;
651     end;
652    
653     procedure TIBSQLMonitorHook.SQLPrepare(qry: TIBSQL);
654     var
655     st: String;
656     begin
657     if FEnabled then
658     begin
659     if not ((tfQPrepare in (FTraceFlags * qry.Database.TraceFlags)) or
660     (tfStmt in (FTraceFlags * qry.Database.TraceFlags))) then
661     Exit;
662     if qry.Owner is TIBCustomDataSet then
663     st := TIBCustomDataSet(qry.Owner).Name
664     else
665     st := qry.Name;
666     st := st + ': [Prepare] ' + qry.SQL.Text + CRLF; {do not localize}
667     st := st + ' Plan: ' + qry.Plan; {do not localize}
668     WriteSQLData(st, tfQPrepare);
669     end;
670     end;
671    
672     procedure TIBSQLMonitorHook.TRCommit(tr: TIBTransaction);
673     var
674     st: String;
675     begin
676     if FEnabled then
677     begin
678     if Assigned(tr.DefaultDatabase) and
679     (not (tfTransact in (FTraceFlags * tr.DefaultDatabase.TraceFlags))) then
680     Exit;
681     st := tr.Name + ': [Commit (Hard commit)]'; {do not localize}
682     WriteSQLData(st, tfTransact);
683     end;
684     end;
685    
686     procedure TIBSQLMonitorHook.TRCommitRetaining(tr: TIBTransaction);
687     var
688     st: String;
689     begin
690     if FEnabled then
691     begin
692     if Assigned(tr.DefaultDatabase) and
693     (not (tfTransact in (FTraceFlags * tr.DefaultDatabase.TraceFlags))) then
694     Exit;
695     st := tr.Name + ': [Commit retaining (Soft commit)]'; {do not localize}
696     WriteSQLData(st, tfTransact);
697     end;
698     end;
699    
700     procedure TIBSQLMonitorHook.TRRollback(tr: TIBTransaction);
701     var
702     st: String;
703     begin
704     if FEnabled then
705     begin
706     if Assigned(tr.DefaultDatabase) and
707     (not (tfTransact in (FTraceFlags * tr.DefaultDatabase.TraceFlags))) then
708     Exit;
709     st := tr.Name + ': [Rollback]'; {do not localize}
710     WriteSQLData(st, tfTransact);
711     end;
712     end;
713    
714     procedure TIBSQLMonitorHook.TRRollbackRetaining(tr: TIBTransaction);
715     var
716     st: String;
717     begin
718     if FEnabled then
719     begin
720     if Assigned(tr.DefaultDatabase) and
721     (not (tfTransact in (FTraceFlags * tr.DefaultDatabase.TraceFlags))) then
722     Exit;
723     st := tr.Name + ': [Rollback retaining (Soft rollback)]'; {do not localize}
724     WriteSQLData(st, tfTransact);
725     end;
726     end;
727    
728     procedure TIBSQLMonitorHook.TRStart(tr: TIBTransaction);
729     var
730     st: String;
731     begin
732     if FEnabled then
733     begin
734     if Assigned(tr.DefaultDatabase) and
735     (not (tfTransact in (FTraceFlags * tr.DefaultDatabase.TraceFlags))) then
736     Exit;
737     st := tr.Name + ': [Start transaction]'; {do not localize}
738     WriteSQLData(st, tfTransact);
739     end;
740     end;
741    
742     procedure TIBSQLMonitorHook.UnregisterMonitor(SQLMonitor: TIBCustomSQLMonitor);
743     var
744     Created : Boolean;
745     begin
746     {$IFDEF DEBUG}writeln('Unregister Monitor');{$ENDIF}
747     if assigned(FReaderThread) then
748     begin
749     FReaderThread.RemoveMonitor(SQLMonitor);
750     if FReaderThread.FMonitors.Count = 0 then
751     begin
752     FReaderThread.Terminate;
753    
754     { There is a possibility of a reader thread, but no writer one.
755     When in that situation, the reader needs to be released after
756     the terminate is set. To do that, create a Writer thread, send
757     the release code (a string of ' ' and type tfMisc) and then free
758     it up. }
759    
760     Created := false;
761     if not Assigned(FWriterThread) then
762     begin
763     FWriterThread := TWriterThread.Create(FGlobalInterface);
764     Created := true;
765     end;
766     FWriterThread.WriteSQLData(' ', tfMisc);
767     {$IFDEF DEBUG}writeln('Wait for read Terminate');{$ENDIF}
768     FReaderThread.WaitFor;
769     if assigned(FReaderThread.FatalException) then
770     IBError(ibxeThreadFailed,['Reader',Exception(FReaderThread.FatalException).Message]);
771     {$IFDEF DEBUG}writeln('Freeing Reader Thread');{$ENDIF}
772     FreeAndNil(FReaderThread);
773     {$IFDEF DEBUG}writeln('Reader Thread Freed');{$ENDIF}
774     if Created then
775     begin
776     FWriterThread.Terminate;
777     {$IFDEF DEBUG}writeln('Wait for write Terminate');{$ENDIF}
778     FWriterThread.WaitFor;
779     if assigned(FWriterThread.FatalException) then
780     IBError(ibxeThreadFailed,['Writer',Exception(FWriterThread.FatalException).Message]);
781     FreeAndNil(FWriterThread);
782     end;
783     end;
784     end;
785     {$IFDEF DEBUG}writeln('Unregister done'){$ENDIF}
786     end;
787    
788     procedure TIBSQLMonitorHook.WriteSQLData(Text: String;
789     DataType: TTraceFlag);
790     begin
791     {$IFDEF DEBUG}writeln('Write SQL Data: '+Text);{$ENDIF}
792     if not assigned(FGlobalInterface) then
793     FGlobalInterface := TGlobalInterface.Create;
794     Text := CRLF + '[Application: ' + ApplicationTitle + ']' + CRLF + Text; {do not localize}
795     if not Assigned(FWriterThread) then
796     FWriterThread := TWriterThread.Create(FGLobalInterface);
797     FWriterThread.WriteSQLData(Text, DataType);
798     end;
799    
800     { TWriterThread }
801    
802     constructor TWriterThread.Create(GlobalInterface: TGlobalInterface);
803    
804     begin
805     inherited Create(true);
806     {$IFDEF DEBUG}writeln('Write Object Created');{$ENDIF}
807     FGlobalInterface := GlobalInterface;
808     FMsgs := TObjectList.Create(true);
809     FCriticalSection := TCriticalSection.Create;
810     FMsgAvailable := TEventObject.Create(FGlobalInterface.Sa,true,false,cWriteMessageAvailable);
811     Start;
812     end;
813    
814     destructor TWriterThread.Destroy;
815     begin
816     if assigned(FMsgs) then FMsgs.Free;
817     if assigned(FCriticalSection) then FCriticalSection.Free;
818     if assigned(FMsgAvailable) then FMsgAvailable.Free;
819     inherited Destroy;
820     end;
821    
822     procedure TWriterThread.Execute;
823     begin
824     {$IFDEF DEBUG}writeln('Write Thread starts');{$ENDIF}
825     try
826     { Place thread code here }
827     while ((not Terminated) and (not bDone)) or
828     (FMsgs.Count <> 0) do
829     begin
830     FMsgAvailable.WaitFor(cMsgWaitTime);
831     { Any one listening? }
832     if FGlobalInterface.MonitorCount = 0 then
833     begin
834     if FMsgs.Count <> 0 then
835     begin
836     {$IFDEF DEBUG}writeln('Write Thread Drop Message');{$ENDIF}
837     RemoveFromList;
838     end;
839     end
840     else
841     { Anything to process? }
842     if FMsgs.Count <> 0 then
843     begin
844     { If the current queued message is a release release the object }
845     if FMsgs.Items[0] is TReleaseObject then
846     begin
847     {$IFDEF DEBUG}writeln('Post Release');{$ENDIF}
848 tony 215 if not Terminated then
849     Synchronize(PostRelease);
850 tony 209 end
851     else
852     { Otherwise write the TraceObject to the buffer }
853     begin
854     WriteToBuffer;
855     end;
856     end
857     else
858     begin
859     FCriticalSection.Enter;
860     try
861     if FMsgs.Count = 0 then
862     FMsgAvailable.ResetEvent
863     finally
864     FCriticalSection.Leave
865     end;
866     end;
867     end;
868     except on E: Exception do
869     begin
870     {$IFDEF DEBUG}writeln('Write Thread raised Exception: ' + E.Message);{$ENDIF}
871     raise
872     end
873     end;
874     {$IFDEF DEBUG}writeln('Write Thread Ends');{$ENDIF}
875     end;
876    
877     procedure TWriterThread.WriteSQLData(Msg : String; DataType: TTraceFlag);
878     begin
879     FCriticalSection.Enter;
880     try
881     FMsgs.Add(TTraceObject.Create(Msg, DataType));
882     finally
883     FCriticalSection.Leave;
884     end;
885     FMsgAvailable.SetEvent
886     end;
887    
888     procedure TWriterThread.BeginWrite;
889     begin
890     {$IFDEF DEBUG}writeln('Begin Write');{$ENDIF}
891     with FGlobalInterface do
892     begin
893     ReadReadyEvent.PassThroughGate; {Wait for readers to become ready }
894     WriterBusyEvent.Lock; {Set Busy State}
895     end;
896     {$IFDEF DEBUG}writeln('Begin Write Complete');{$ENDIF}
897     end;
898    
899     procedure TWriterThread.EndWrite;
900     begin
901     {$IFDEF DEBUG}writeln('End Write');{$ENDIF}
902     with FGlobalInterface do
903     begin
904     DataAvailableEvent.Unlock; { Signal Data Available. }
905     ReadFinishedEvent.PassThroughGate; {Wait for readers to finish }
906     DataAvailableEvent.Lock; {reset Data Available }
907     WriterBusyEvent.Unlock; {Signal not Busy }
908     end;
909     {$IFDEF DEBUG}writeln('End Write Complete');{$ENDIF}
910     end;
911    
912     procedure TWriterThread.WriteToBuffer;
913     var I, len: integer;
914     Temp: TTraceObject;
915     begin
916     {$IFDEF DEBUG}writeln('Write to Buffer');{$ENDIF}
917     FGlobalInterface.WriteLock.Lock;
918     try
919     { If there are no monitors throw out the message
920     The alternative is to have messages queue up until a
921     monitor is ready.}
922    
923     if FGlobalInterface.MonitorCount = 0 then
924     RemoveFromList
925     else
926     begin
927     i := 1;
928     len := Length(TTraceObject(FMsgs[0]).FMsg);
929     if len <= FGlobalInterface.MaxBufferSize then
930     begin
931     BeginWrite;
932     try
933     FGlobalInterface.SendTrace(TTraceObject(FMsgs[0]))
934     finally
935     RemoveFromList;
936     EndWrite
937     end;
938     end
939     else
940     try
941     while len > 0 do
942     begin
943     {$IFDEF DEBUG}writeln('Sending Partial Message, len = ',len);{$ENDIF}
944     Temp := TTraceObject.Create(TTraceObject(FMsgs[0]),i,Min(len,FGlobalInterface.MaxBufferSize));
945     try
946     BeginWrite;
947     FGlobalInterface.SendTrace(Temp);
948     Inc(i,FGlobalInterface.MaxBufferSize);
949     Dec(len,FGlobalInterface.MaxBufferSize);
950     finally
951     Temp.Free;
952     EndWrite
953     end
954     end;
955     finally
956     RemoveFromList;
957     end
958     end;
959     finally
960     FGlobalInterface.WriteLock.Unlock;
961     end;
962     {$IFDEF DEBUG}writeln('Done Write');{$ENDIF}
963     end;
964    
965     procedure TWriterThread.RemoveFromList;
966     begin
967     {$IFDEF DEBUG}writeln('Write Thread: Remove object From List');{$ENDIF}
968     FCriticalSection.Enter;
969     try
970     FMsgs.Remove(FMsgs[0]); { Pop the written item }
971     finally
972     FCriticalSection.Leave;
973     end;
974     end;
975    
976     procedure TWriterThread.PostRelease;
977     var Monitor: TIBCustomSQLMonitor;
978     begin
979     Monitor := TReleaseObject(FMsgs.Items[0]).FMonitor;
980     Monitor.ReleaseObject
981     end;
982    
983     procedure TWriterThread.ReleaseMonitor(Arg : TIBCustomSQLMonitor);
984     begin
985     FMsgs.Add(TReleaseObject.Create(Arg));
986     end;
987    
988     { TTraceObject }
989    
990     constructor TTraceObject.Create(Msg : String; DataType: TTraceFlag);
991     begin
992     FMsg := Msg;
993     FDataType := DataType;
994     FTimeStamp := Now;
995     end;
996    
997     constructor TTraceObject.Create(obj: TTraceObject);
998     begin
999     FMsg := obj.FMsg;
1000     FDataType := obj.FDataType;
1001     FTimeStamp := obj.FTimeStamp;
1002     end;
1003    
1004     constructor TTraceObject.Create(obj: TTraceObject; MsgOffset, MsgLen: integer);
1005     begin
1006     FDataType := obj.FDataType;
1007     FTimeStamp := obj.FTimeStamp;
1008     FMsg := copy(obj.FMsg,MsgOffset,MsgLen)
1009     end;
1010    
1011     { TReleaseObject }
1012    
1013     constructor TReleaseObject.Create(Monitor : TIBCustomSQLMonitor);
1014     begin
1015     FMonitor := Monitor;
1016     end;
1017    
1018     { ReaderThread }
1019    
1020     procedure TReaderThread.AddMonitor(Arg: TIBCustomSQLMonitor);
1021     begin
1022     FCriticalSection.Enter;
1023     try
1024     if FMonitors.IndexOf(Arg) < 0 then
1025     FMonitors.Add(Arg);
1026     finally
1027     FCriticalSection.Leave
1028     end;
1029     end;
1030    
1031     procedure TReaderThread.AlertMonitors;
1032     var i : Integer;
1033     FTemp : TTraceObject;
1034     Monitor: TIBCustomSQLMonitor;
1035     begin
1036     for i := 0 to FMonitors.Count - 1 do
1037     begin
1038     {$IFDEF DEBUG}writeln('Sending Message to Monitor ' +IntToStr(i));{$ENDIF}
1039     FTemp := TTraceObject.Create(st);
1040     Monitor := TIBCustomSQLMonitor(FMonitors[i]);
1041     Monitor.ReceiveMessage(FTemp);
1042     end;
1043     end;
1044    
1045     procedure TReaderThread.BeginRead;
1046     begin
1047     {$IFDEF DEBUG}writeln('Begin Read');{$ENDIF}
1048     with FGlobalInterface do
1049     begin
1050     WriterBusyEvent.PassthroughGate; { Wait for Writer not busy}
1051     ReadFinishedEvent.Lock; { Prepare Read Finished Gate}
1052     ReadReadyEvent.Unlock; { Signal read ready }
1053     {$IFDEF DEBUG}writeln('Read Ready Unlocked');{$ENDIF}
1054     DataAvailableEvent.PassthroughGate; { Wait for a Data Available }
1055     end;
1056     {$IFDEF DEBUG}writeln('Begin Read Complete');{$ENDIF}
1057     end;
1058    
1059     constructor TReaderThread.Create(GlobalInterface: TGlobalInterface);
1060     begin
1061     inherited Create(true);
1062     FGlobalInterface := GlobalInterface;
1063     st := TTraceObject.Create('', tfMisc);
1064     FGlobalInterface.IncMonitorCount;
1065     FMonitors := TObjectList.Create(false);
1066     FCriticalSection := TCriticalSection.Create;
1067     {$IFDEF DEBUG}writeln('Reader Thread Created');{$ENDIF}
1068     FGlobalInterface.ReadReadyEvent.Lock; { Initialise Read Ready}
1069     Start;
1070     end;
1071    
1072     destructor TReaderThread.Destroy;
1073     begin
1074     {$IFDEF DEBUG}writeln('Reader Thread Destory');{$ENDIF}
1075     FGlobalInterface.ReadReadyEvent.UnLock;
1076     if assigned(FGlobalInterface) and (FGlobalInterface.MonitorCount > 0) then
1077     FGlobalInterface.DecMonitorCount;
1078     FMonitors.Free;
1079     if assigned(FCriticalSection) then FCriticalSection.Free;
1080     st.Free;
1081     inherited Destroy;
1082     end;
1083    
1084     procedure TReaderThread.EndRead;
1085     begin
1086     {$IFDEF DEBUG}writeln('End Read');{$ENDIF}
1087     FGlobalInterface.ReadReadyEvent.Lock; { reset Read Ready}
1088     FGlobalInterface.ReadFinishedEvent.Unlock; {Signal Read completed }
1089     {$IFDEF DEBUG}writeln('End Read Complete');{$ENDIF}
1090     end;
1091    
1092     procedure TReaderThread.Execute;
1093     begin
1094     {$IFDEF DEBUG}writeln('Read Thread Starts');{$ENDIF}
1095     { Place thread code here }
1096     while (not Terminated) and (not bDone) do
1097     begin
1098     ReadSQLData;
1099     if (st.FMsg <> '') and
1100     not ((st.FMsg = ' ') and (st.FDataType = tfMisc)) then
1101     begin
1102     {$IFDEF DEBUG}writeln('Sending Message to Monitors');{$ENDIF}
1103 tony 215 if not Terminated then
1104     Synchronize(AlertMonitors);
1105 tony 209 end;
1106     end;
1107     {$IFDEF DEBUG}writeln('Read Thread Ends');{$ENDIF}
1108     end;
1109    
1110     procedure TReaderThread.ReadSQLData;
1111     begin
1112     st.FMsg := '';
1113     BeginRead;
1114     if not bDone then
1115     try
1116     FGlobalInterface.ReceiveTrace(st)
1117     finally
1118     EndRead;
1119     end;
1120     end;
1121    
1122     procedure TReaderThread.RemoveMonitor(Arg: TIBCustomSQLMonitor);
1123     begin
1124     FCriticalSection.Enter;
1125     try
1126     FMonitors.Remove(Arg);
1127     finally
1128     FCriticalSection.Leave
1129     end;
1130     end;
1131    
1132     { Misc methods }
1133    
1134     function MonitorHook: IIBSQLMonitorHook;
1135     begin
1136     if (_MonitorHook = nil) and (not bDone) then
1137     begin
1138     CS.Enter;
1139     if (_MonitorHook = nil) and (not bDone) then
1140     begin
1141     _MonitorHook := TIBSQLMonitorHook.Create;
1142     _MonitorHook._AddRef;
1143     end;
1144     CS.Leave;
1145     end;
1146     result := _MonitorHook;
1147     end;
1148    
1149     procedure EnableMonitoring;
1150     begin
1151     MonitorHook.Enabled := True;
1152     end;
1153    
1154     procedure DisableMonitoring;
1155     begin
1156     MonitorHook.Enabled := False;
1157     end;
1158    
1159     function MonitoringEnabled: Boolean;
1160     begin
1161     result := MonitorHook.Enabled;
1162     end;
1163    
1164     procedure CloseThreads;
1165     begin
1166     {$IFDEF DEBUG}writeln('Closed Threads Called');{$ENDIF}
1167     if Assigned(FReaderThread) then
1168     begin
1169     FReaderThread.Terminate;
1170     FReaderThread.WaitFor;
1171     FreeAndNil(FReaderThread);
1172     end;
1173     if Assigned(FWriterThread) then
1174     begin
1175     FWriterThread.Terminate;
1176     FWriterThread.WaitFor;
1177     FreeAndNil(FWriterThread);
1178     end;
1179     end;
1180    
1181     initialization
1182     CS := TCriticalSection.Create;
1183     _MonitorHook := nil;
1184     FWriterThread := nil;
1185     FReaderThread := nil;
1186     bDone := False;
1187     {$IFDEF USE_SV5_IPC}
1188     if GetEnvironmentVariable('FBSQL_IPCFILENAME') <> '' then
1189     IPCFileName := GetEnvironmentVariable('FBSQL_IPCFILENAME')
1190     else
1191     IPCFileName := GetTempDir(true) + IPCFileName + '.' + GetEnvironmentVariable('USER');
1192     {$ENDIF}
1193    
1194     finalization
1195     {$IFDEF DEBUG}writeln('Entered Finalisation');{$ENDIF}
1196     try
1197     { Write an empty string to force the reader to unlock during termination }
1198     bDone := True;
1199     if Assigned(_MonitorHook) then
1200     _MonitorHook.ForceRelease;
1201     CloseThreads;
1202     if Assigned(_MonitorHook) then
1203     _MonitorHook._Release;
1204    
1205     finally
1206     _MonitorHook := nil;
1207     if assigned(CS) then CS.Free;
1208     end;
1209     end.