profiler.inc 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257
  1. // Copyright (C) 2012 Zeex
  2. //
  3. // Permission is hereby granted, free of charge, to any person obtaining a
  4. // copy of this software and associated documentation files (the "Software"),
  5. // to deal in the Software without restriction, including without limitation
  6. // the rights to use, copy, modify, merge, publish, distribute, sublicense,
  7. // and/or sell copies of the Software, and to permit persons to whom the
  8. // Software is furnished to do so, subject to the following conditions:
  9. //
  10. // The above copyright notice and this permission notice shall be included in
  11. // all copies or substantial portions of the Software.
  12. //
  13. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
  14. // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  15. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  16. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  17. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  18. // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
  19. // DEALINGS IN THE SOFTWARE.
  20. #if defined PROFILER_INC
  21. #endinput
  22. #endif
  23. #define PROFILER_INC
  24. #include <file>
  25. #include <time>
  26. #include <string>
  27. #tryinclude <console>
  28. #include "amx_base"
  29. #include "amx_header"
  30. #include "opcode"
  31. // Call stack size (maximum call depth).
  32. #if !defined PROF_MAX_CALL_STACK
  33. #define PROF_MAX_CALL_STACK 10
  34. #endif
  35. // Maximum number of profiled publics.
  36. #if !defined PROF_MAX_PUBLICS
  37. #define PROF_MAX_PUBLICS 1000
  38. #endif
  39. enum ProfEntryCode {
  40. Opcode:pec_push_c0,
  41. pec_push_address,
  42. Opcode:pec_push_c1,
  43. pec_push_index,
  44. Opcode:pec_push_c2,
  45. pec_push_8,
  46. Opcode:pec_call,
  47. pec_call_enter
  48. }
  49. static stock g_pecs[PROF_MAX_PUBLICS][ProfEntryCode];
  50. static stock g_num_pecs;
  51. enum ProfCallInfo {
  52. pci_index,
  53. pci_start_time
  54. }
  55. enum ProfPublicInfo {
  56. ppi_child_time,
  57. ppi_total_time,
  58. ppi_num_calls
  59. }
  60. static stock g_publics[PROF_MAX_PUBLICS][ProfPublicInfo];
  61. static stock g_num_publics;
  62. static stock g_call_stack[PROF_MAX_CALL_STACK][ProfCallInfo];
  63. static stock g_call_depth;
  64. static stock exit_public() {
  65. if (--g_call_depth < sizeof(g_call_stack)) {
  66. new index = g_call_stack[g_call_depth][pci_index];
  67. new parent = -1;
  68. if (g_call_depth > 0) {
  69. parent = g_call_stack[g_call_depth-1][pci_index];
  70. }
  71. if (index < g_num_publics) {
  72. new i = g_call_depth;
  73. new tick = tickcount();
  74. new time = tick - g_call_stack[i][pci_start_time];
  75. if (time < 0) {
  76. // Work around negative intervals due to tick count overflow
  77. if (tick < 0) {
  78. time = (tick - cellmin) + (cellmax - g_call_stack[i][pci_start_time]);
  79. }
  80. }
  81. if (time > 0) {
  82. g_publics[index][ppi_total_time] += time;
  83. if (parent > 0) {
  84. g_publics[parent][ppi_child_time] += time;
  85. }
  86. }
  87. g_publics[index][ppi_num_calls]++;
  88. }
  89. //{
  90. // new public_name[32];
  91. // GetPublicNameByIndex(index, public_name);
  92. // printf("Leaving %s", public_name);
  93. //}
  94. }
  95. #emit halt 0
  96. return 0; // make compiler happy
  97. }
  98. static stock enter_public(index, address) {
  99. //{
  100. // new public_name[32];
  101. // GetPublicNameByIndex(index, public_name);
  102. // printf("Entering %s", public_name);
  103. //}
  104. if (g_call_depth < sizeof(g_call_stack)) {
  105. new pci[ProfCallInfo];
  106. pci[pci_index] = index;
  107. pci[pci_start_time] = tickcount();
  108. g_call_stack[g_call_depth] = pci;
  109. } else {
  110. printf("profiler warning: PROF_MAX_CALL_STACK is set to %d but current level is %d", PROF_MAX_CALL_STACK, g_call_depth);
  111. }
  112. // Pop locals + arguments + numbytes + return address + frm.
  113. #emit stack 20
  114. ++g_call_depth;
  115. // modify public's return address so it will jump to exit_public() when done
  116. #emit stack 4
  117. #emit const.pri exit_public
  118. #emit lctrl 8
  119. #emit push.pri
  120. // jump to the public
  121. #emit load.s.pri address
  122. #emit jump.pri
  123. return 0; // make compiler happy
  124. }
  125. static stock new_pec(index, address, code_start) {
  126. if (g_num_pecs < sizeof(g_pecs)) {
  127. new pec[ProfEntryCode];
  128. pec[pec_push_c0] = RelocateOpcode(OP_PUSH_C);
  129. pec[pec_push_address] = address;
  130. pec[pec_push_c1] = RelocateOpcode(OP_PUSH_C);
  131. pec[pec_push_index] = index;
  132. pec[pec_push_c2] = RelocateOpcode(OP_PUSH_C);
  133. pec[pec_push_8] = 8;
  134. new enter_proc;
  135. #emit const.pri enter_public
  136. #emit stor.s.pri enter_proc
  137. pec[pec_call] = RelocateOpcode(OP_CALL);
  138. pec[pec_call_enter] = code_start + enter_proc;
  139. g_pecs[g_num_pecs] = pec;
  140. return g_num_pecs++;
  141. }
  142. return -1;
  143. }
  144. stock ProfilerInit() {
  145. new hdr[AMX_HDR];
  146. GetAmxHeader(hdr);
  147. new publics = hdr[AMX_HDR_PUBLICS] - hdr[AMX_HDR_DAT];
  148. new defsize = hdr[AMX_HDR_DEFSIZE];
  149. new num_publics = (hdr[AMX_HDR_NATIVES] - hdr[AMX_HDR_PUBLICS]) / defsize;
  150. new amx_base = GetAmxBaseAddress();
  151. // Redirect all public calls to ProfileHook().
  152. for (new i = 0, cur = publics; i < num_publics; cur += defsize, i++) {
  153. new address;
  154. #emit lref.s.pri cur
  155. #emit stor.s.pri address
  156. new pec_index = new_pec(i, address, amx_base + hdr[AMX_HDR_COD]);
  157. if (pec_index < 0) {
  158. printf("profiler warning: Too many public functions (%d). Consider increasing PROF_MAX_PUBLICS.", num_publics);
  159. break;
  160. }
  161. // Get start address of the g_pecs sub-array at pec_index.
  162. new pec_start;
  163. #emit const.alt g_pecs
  164. #emit load.s.pri pec_index
  165. #emit idxaddr
  166. #emit move.alt
  167. #emit load.i
  168. #emit add
  169. #emit stor.s.pri pec_start
  170. // Alter public table to redirect calls to the PEC.
  171. HookPublic(i, pec_start + hdr[AMX_HDR_DAT] - hdr[AMX_HDR_COD]);
  172. g_publics[i][ppi_child_time] = 0;
  173. g_publics[i][ppi_total_time] = 0;
  174. g_publics[i][ppi_num_calls] = 0;
  175. g_num_publics++;
  176. }
  177. }
  178. stock bool:ProfilerWriteData(const filename[]) {
  179. new File:file = fopen(filename, io_write);
  180. new buffer[100];
  181. if (!file) {
  182. return false;
  183. }
  184. fwrite(file, "+----------------------------------+----------------+----------------+----------------+\n");
  185. fwrite(file, "| name | calls | self_time | total_time |\n");
  186. fwrite(file, "+----------------------------------+----------------+----------------+----------------+\n");
  187. for (new i = 0; i < g_num_publics; i++) {
  188. new name[32];
  189. GetPublicNameFromIndex(i, name);
  190. #if defined strformat
  191. strformat(buffer, sizeof(buffer), false, "| %32s |%15d |%15d |%15d |\n",
  192. name,
  193. g_publics[i][ppi_num_calls],
  194. g_publics[i][ppi_total_time] - g_publics[i][ppi_child_time],
  195. g_publics[i][ppi_total_time]
  196. );
  197. #elseif defined format
  198. format(buffer, sizeof(buffer), "| %32s |%15d |%15d |%15d |\n",
  199. name,
  200. g_publics[i][ppi_num_calls],
  201. g_publics[i][ppi_total_time] - g_publics[i][ppi_child_time],
  202. g_publics[i][ppi_total_time]
  203. );
  204. #else
  205. // Generate proper compiler errors for the missing symbols - ONLY IF the function is
  206. // actually used.
  207. format(buffer, 0, "You need this function or strformat");
  208. strformat(buffer, 0, false, "You need this function or format");
  209. #endif
  210. fwrite(file, buffer);
  211. fwrite(file, "+----------------------------------+----------------+----------------+----------------+\n");
  212. }
  213. fclose(file);
  214. return true;
  215. }