ViewVC Help
View File | Revision Log | Show Annotations | Download File | View Changeset | Root Listing
root/public/ibx/trunk/runtime/IBSQLMonitor.pas
Revision: 39
Committed: Tue May 17 08:14:52 2016 UTC (8 years, 6 months ago) by tony
Content type: text/x-pascal
File size: 31626 byte(s)
Log Message:
Committing updates for Release R1-4-1

File Contents

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