ViewVC Help
View File | Revision Log | Show Annotations | Download File | View Changeset | Root Listing
root/public/ibx/trunk/runtime/nongui/IBSQLMonitor.pas
Revision: 209
Committed: Wed Mar 14 12:48:51 2018 UTC (6 years, 1 month ago) by tony
Content type: text/x-pascal
File size: 34032 byte(s)
Log Message:
Fixes Merged

File Contents

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