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