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