ViewVC Help
View File | Revision Log | Show Annotations | Download File | View Changeset | Root Listing
root/public/ibx/trunk/runtime/nongui/IBSQLMonitor.pas
Revision: 323
Committed: Thu Feb 25 12:14:35 2021 UTC (3 years, 2 months ago) by tony
Content type: text/x-pascal
File size: 32178 byte(s)
Log Message:
Fixed Merged

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