ViewVC Help
View File | Revision Log | Show Annotations | Download File | View Changeset | Root Listing
root/public/ibx/trunk/runtime/nongui/IBSQLMonitor.pas
Revision: 315
Committed: Thu Feb 25 11:56:36 2021 UTC (3 years, 9 months ago) by tony
Content type: text/x-pascal
File size: 33672 byte(s)
Log Message:
Updated for IBX 4 release

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