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 (7 years, 11 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

# Content
1 {************************************************************************}
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 {$IF FPC_FULLVERSION >= 20700 }
45 {$codepage UTF8}
46 {$ENDIF}
47
48 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 IPCFileName := GetTempDir(true) + IPCFileName + '.' + strpas(FpGetEnv('USER'));
1110 {$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.