Non puoi selezionare più di 25 argomenti Gli argomenti devono iniziare con una lettera o un numero, possono includere trattini ('-') e possono essere lunghi fino a 35 caratteri.

304 righe
6.6 KiB

  1. unit uutlEmbeddedProfiler;
  2. {$mode objfpc}{$H+}
  3. {$OPTIMIZATION ON}
  4. {$OPTIMIZATION REGVAR}
  5. {$OPTIMIZATION PEEPHOLE}
  6. {$OPTIMIZATION CSE}
  7. interface
  8. uses
  9. SysUtils;
  10. var
  11. ProfilerEnabled: boolean;
  12. procedure ProfilerEnterProc(const Addr: Pointer);
  13. procedure ProfilerEnterProc(const Addr: Pointer; const aName: String);
  14. procedure ProfilerLeaveProc;
  15. implementation
  16. {$I uutlEmbeddedProfiler.inc}
  17. {$IFDEF PROFILER_ENABLE}
  18. uses
  19. Windows, Classes, uutlStreamHelper;
  20. type
  21. TWriterThread = class(TThread)
  22. private
  23. fPF: Int64;
  24. procedure SaveCurrentWrite;
  25. public
  26. constructor Create;
  27. destructor Destroy; override;
  28. procedure Execute; override;
  29. end;
  30. PEventRecord = ^TEventRecord;
  31. TEventRecord = packed record
  32. Name: String;
  33. Func: PtrUInt;
  34. Thread: TThreadID;
  35. When: Int64;
  36. end;
  37. TProfileDataFile = class
  38. private
  39. fDF: TStream;
  40. public
  41. constructor Create(const aFileName: string);
  42. destructor Destroy; override;
  43. procedure WriteEnter(Thread: TThreadID; When: Int64; Func: PtrUInt; Name: string);
  44. procedure WriteLeave(Thread: TThreadID; When: Int64);
  45. end;
  46. {$I uutlProfilerBinaryFmt.inc}
  47. const
  48. MAX_EVENT_COUNT = 1000;
  49. RETURN_FUNCTION : PtrUInt = PtrUInt(-1);
  50. var
  51. ProfilerDataFile: TProfileDataFile;
  52. LineNumberComp: PtrUInt;
  53. Events: array[0..MAX_EVENT_COUNT-1] of TEventRecord;
  54. InsertPtr, WritePtr: Integer;
  55. WriterThread: TWriterThread;
  56. SLInsert: Cardinal;
  57. procedure InstallWriterThread;
  58. begin
  59. if not Assigned(WriterThread) then
  60. WriterThread:= TWriterThread.Create;
  61. end;
  62. procedure UninstallWriterThread;
  63. begin
  64. if Assigned(WriterThread) then begin
  65. WriterThread.Terminate;
  66. WriterThread.WaitFor;
  67. FreeAndNil(WriterThread);
  68. end;
  69. end;
  70. procedure NextInsert;
  71. begin
  72. inc(InsertPtr);
  73. if InsertPtr >= MAX_EVENT_COUNT then
  74. InsertPtr:= 0;
  75. // wait until writer cleared this element
  76. While Events[InsertPtr].Func <> 0 do
  77. ThreadSwitch;
  78. end;
  79. procedure CalibrateLineNumberCompensation1(const Addr: PtrUInt);
  80. begin
  81. LineNumberComp:= Addr;
  82. end;
  83. procedure CalibrateLineNumberCompensation;
  84. label
  85. mark;
  86. begin
  87. mark:
  88. CalibrateLineNumberCompensation1({%H-}PtrUInt(Get_pc_addr));
  89. //measure out one CALL
  90. LineNumberComp:= LineNumberComp - {%H-}PtrUInt(@mark);
  91. //go somewhere into the stack prep before the call
  92. inc(LineNumberComp);
  93. end;
  94. procedure ProfilerEnterProc(const Addr: Pointer);
  95. begin
  96. ProfilerEnterProc(Addr, '');
  97. end;
  98. procedure ProfilerEnterProc(const Addr: Pointer; const aName: String);
  99. var
  100. f: PtrUInt;
  101. tid: TThreadID;
  102. er: PEventRecord;
  103. begin
  104. if not ProfilerEnabled then
  105. exit;
  106. tid:= GetCurrentThreadId;
  107. InstallWriterThread;
  108. repeat
  109. System.InterlockedCompareExchange(SLInsert, tid, 0);
  110. until SLInsert = tid;
  111. try
  112. // measure as late (close to measured code) as possible, but still write .Func last, because that's our lockvar
  113. f:= {%H-}PtrUInt(addr) - LineNumberComp;
  114. er:= @Events[InsertPtr];
  115. er^.Thread := tid;
  116. er^.Name := aName;
  117. QueryPerformanceCounter(er^.When);
  118. er^.Func := f;
  119. NextInsert;
  120. finally
  121. System.InterLockedExchange(SLInsert, 0);
  122. end;
  123. end;
  124. procedure ProfilerLeaveProc;
  125. var
  126. t: Int64;
  127. tid: TThreadID;
  128. er: PEventRecord;
  129. begin
  130. if not ProfilerEnabled then
  131. exit;
  132. QueryPerformanceCounter(t{%H-});
  133. tid:= GetCurrentThreadId;
  134. repeat
  135. System.InterlockedCompareExchange(SLInsert, tid, 0);
  136. until SLInsert = tid;
  137. try
  138. // measure as early (close to measured code) as possible, but still write .Func last, because that's our lockvar
  139. er := @Events[InsertPtr];
  140. er^.Thread := tid;
  141. er^.When := t;
  142. er^.Name := '';
  143. er^.Func := RETURN_FUNCTION;
  144. NextInsert;
  145. finally
  146. System.InterLockedExchange(SLInsert, 0);
  147. end;
  148. end;
  149. { TWriterThread }
  150. constructor TWriterThread.Create;
  151. begin
  152. inherited Create(false);
  153. QueryPerformanceFrequency(fPF);
  154. end;
  155. destructor TWriterThread.Destroy;
  156. begin
  157. inherited Destroy;
  158. end;
  159. procedure TWriterThread.Execute;
  160. begin
  161. while not Terminated do begin
  162. while Events[WritePtr].Func<>0 do begin
  163. SaveCurrentWrite;
  164. inc(WritePtr);
  165. if WritePtr >= MAX_EVENT_COUNT then
  166. WritePtr:= 0;
  167. end;
  168. Sleep(1);
  169. end;
  170. //finish up remaining data, by now, writing is disabled
  171. while Events[WritePtr].Func<>0 do begin
  172. SaveCurrentWrite;
  173. inc(WritePtr);
  174. if WritePtr >= MAX_EVENT_COUNT then
  175. WritePtr:= 0;
  176. end;
  177. end;
  178. procedure TWriterThread.SaveCurrentWrite;
  179. begin
  180. if Events[WritePtr].Func = 0 then
  181. exit;
  182. if Events[WritePtr].Func = RETURN_FUNCTION then
  183. ProfilerDataFile.WriteLeave(Events[WritePtr].Thread, (Events[WritePtr].When * 1000 * 1000) div fPF)
  184. else begin
  185. ProfilerDataFile.WriteEnter(Events[WritePtr].Thread, (Events[WritePtr].When * 1000 * 1000) div fPF, Events[WritePtr].Func, Events[WritePtr].Name);
  186. end;
  187. Events[WritePtr].Func:= 0;
  188. end;
  189. { TProfileDataFile }
  190. constructor TProfileDataFile.Create(const aFileName: string);
  191. var
  192. H: TPBHeader;
  193. hh: THandle;
  194. begin
  195. inherited Create;
  196. fDF:= TutlPagedBufferStream.Create(TFileStream.Create(aFileName, fmCreate), 4096*64, true);
  197. H.VersionMagic:= NtoBE(HEADER_VER_MAGIC);
  198. H.PtrSize:= Sizeof(PtrUInt);
  199. H.ProgramName:= ExtractFileName(ParamStr(0));
  200. hh:= FileOpen(ParamStr(0), fmOpenRead or fmShareDenyNone);
  201. try
  202. H.ProgramFileSize:= FileSeek(hh, 0, soFromEnd);
  203. finally
  204. FileClose(hh);
  205. end;
  206. fDF.Write(H, sizeof(H));
  207. end;
  208. destructor TProfileDataFile.Destroy;
  209. begin
  210. FreeAndNil(fDF);
  211. inherited;
  212. end;
  213. procedure TProfileDataFile.WriteEnter(Thread: TThreadID; When: Int64; Func: PtrUInt; Name: string);
  214. var
  215. H: TPBEventHeader;
  216. R: TPBEventEnterProc;
  217. begin
  218. H.ThreadID:= Thread;
  219. H.Timestamp:= When;
  220. H.Kind:= PB_KIND_ENTER;
  221. fDF.Write(H, Sizeof(H));
  222. R.Func:= Func;
  223. R.NameLen:= Length(Name);
  224. fDF.Write(R, Sizeof(R));
  225. if R.NameLen > 0 then
  226. fDF.Write(Name[1], R.NameLen);
  227. end;
  228. procedure TProfileDataFile.WriteLeave(Thread: TThreadID; When: Int64);
  229. var
  230. H: TPBEventHeader;
  231. begin
  232. H.ThreadID:= Thread;
  233. H.Timestamp:= When;
  234. H.Kind:= PB_KIND_EXIT;
  235. fDF.Write(H, Sizeof(H));
  236. end;
  237. {$ELSE}
  238. procedure ProfilerEnterProc(const Addr: Pointer); inline;
  239. begin end;
  240. procedure ProfilerEnterProc(const Addr: Pointer; const aName: String); inline;
  241. begin end;
  242. procedure ProfilerLeaveProc; inline;
  243. begin end;
  244. {$ENDIF}
  245. initialization
  246. {$IFDEF PROFILER_ENABLE}
  247. ProfilerEnabled:= false;
  248. InsertPtr:= 0;
  249. WritePtr:= 0;
  250. WriterThread:= nil;
  251. CalibrateLineNumberCompensation;
  252. ProfilerDataFile:= TProfileDataFile.Create(ChangeFileExt(ParamStr(0), '.profbin'));
  253. ProfilerEnabled:= true;
  254. {$ENDIF}
  255. finalization
  256. {$IFDEF PROFILER_ENABLE}
  257. ProfilerEnabled:= false;
  258. UninstallWriterThread;
  259. FreeAndNil(ProfilerDataFile);
  260. {$ENDIF}
  261. end.