profiler.inc 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234
  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 <a_samp>
  25. #include "amx_base"
  26. #include "amx_header"
  27. #include "opcode"
  28. // Call stack size (maximum call depth).
  29. #if !defined PROF_MAX_CALL_STACK
  30. #define PROF_MAX_CALL_STACK 10
  31. #endif
  32. // Maximum number of profiled publics.
  33. #if !defined PROF_MAX_PUBLICS
  34. #define PROF_MAX_PUBLICS 1000
  35. #endif
  36. enum ProfEntryCode {
  37. pec_push_address[2],
  38. pec_push_index[2],
  39. pec_push_8[2],
  40. pec_call_enter[2]
  41. }
  42. static stock g_pecs[PROF_MAX_PUBLICS][ProfEntryCode];
  43. static stock g_num_pecs;
  44. enum ProfCallInfo {
  45. pci_index,
  46. pci_start_time
  47. }
  48. enum ProfPublicInfo {
  49. ppi_child_time,
  50. ppi_total_time,
  51. ppi_num_calls
  52. }
  53. static stock g_publics[PROF_MAX_PUBLICS][ProfPublicInfo];
  54. static stock g_num_publics;
  55. static stock g_call_stack[PROF_MAX_CALL_STACK][ProfCallInfo];
  56. static stock g_call_depth;
  57. static stock exit_public() {
  58. if (--g_call_depth < sizeof(g_call_stack)) {
  59. new index = g_call_stack[g_call_depth][pci_index];
  60. new parent = -1;
  61. if (g_call_depth > 0) {
  62. parent = g_call_stack[g_call_depth-1][pci_index];
  63. }
  64. if (index < g_num_publics) {
  65. new i = g_call_depth;
  66. new tick = GetTickCount();
  67. new time = tick - g_call_stack[i][pci_start_time];
  68. if (time < 0) {
  69. // Work around negative intervals due to tick count overflow
  70. if (tick < 0) {
  71. time = (tick - cellmin) + (cellmax - g_call_stack[i][pci_start_time]);
  72. }
  73. }
  74. if (time > 0) {
  75. g_publics[index][ppi_total_time] += time;
  76. if (parent > 0) {
  77. g_publics[parent][ppi_child_time] += time;
  78. }
  79. }
  80. g_publics[index][ppi_num_calls]++;
  81. }
  82. //{
  83. // new public_name[32];
  84. // GetPublicNameByIndex(index, public_name);
  85. // printf("Leaving %s", public_name);
  86. //}
  87. }
  88. #emit halt 0
  89. return 0; // make compiler happy
  90. }
  91. static stock enter_public(index, address) {
  92. //{
  93. // new public_name[32];
  94. // GetPublicNameByIndex(index, public_name);
  95. // printf("Entering %s", public_name);
  96. //}
  97. if (g_call_depth < sizeof(g_call_stack)) {
  98. new pci[ProfCallInfo];
  99. pci[pci_index] = index;
  100. pci[pci_start_time] = GetTickCount();
  101. g_call_stack[g_call_depth] = pci;
  102. } else {
  103. printf("profiler warning: PROF_MAX_CALL_STACK is set to %d but current level is %d", PROF_MAX_CALL_STACK, g_call_depth);
  104. }
  105. // Pop locals + arguments + numbytes + return address + frm.
  106. #emit stack 20
  107. ++g_call_depth;
  108. // modify public's return address so it will jump to exit_public() when done
  109. #emit stack 4
  110. #emit push.c exit_public
  111. // jump to the public
  112. #emit load.s.pri address
  113. #emit jump.pri
  114. return 0; // make compiler happy
  115. }
  116. static stock new_pec(index, address, code_start) {
  117. if (g_num_pecs < sizeof(g_pecs)) {
  118. new pec[ProfEntryCode];
  119. pec[pec_push_address][0] = RelocateOpcode(OP_PUSH_C);
  120. pec[pec_push_address][1] = address;
  121. pec[pec_push_index][0] = RelocateOpcode(OP_PUSH_C);
  122. pec[pec_push_index][1] = index;
  123. pec[pec_push_8][0] = RelocateOpcode(OP_PUSH_C);
  124. pec[pec_push_8][1] = 8;
  125. new enter_proc;
  126. #emit const.pri enter_public
  127. #emit stor.s.pri enter_proc
  128. pec[pec_call_enter][0] = RelocateOpcode(OP_CALL);
  129. pec[pec_call_enter][1] = code_start + enter_proc;
  130. g_pecs[g_num_pecs] = pec;
  131. return g_num_pecs++;
  132. }
  133. return -1;
  134. }
  135. stock ProfilerInit() {
  136. new hdr[AMX_HDR];
  137. GetAmxHeader(hdr);
  138. new publics = hdr[AMX_HDR_PUBLICS] - hdr[AMX_HDR_DAT];
  139. new defsize = hdr[AMX_HDR_DEFSIZE];
  140. new num_publics = (hdr[AMX_HDR_NATIVES] - hdr[AMX_HDR_PUBLICS]) / defsize;
  141. new amx_base = GetAmxBase();
  142. // Redirect all public calls to ProfileHook().
  143. for (new i = 0, cur = publics; i < num_publics; cur += defsize, i++) {
  144. new address;
  145. #emit lref.s.pri cur
  146. #emit stor.s.pri address
  147. new pec_index = new_pec(i, address, amx_base + hdr[AMX_HDR_COD]);
  148. if (pec_index < 0) {
  149. printf("profiler warning: Too many public functions (%d). Consider increasing PROF_MAX_PUBLICS.", num_publics);
  150. break;
  151. }
  152. // Get start address of the g_pecs sub-array at pec_index.
  153. new pec_start;
  154. #emit const.alt g_pecs
  155. #emit load.s.pri pec_index
  156. #emit idxaddr
  157. #emit move.alt
  158. #emit load.i
  159. #emit add
  160. #emit stor.s.pri pec_start
  161. // Alter public table to redirect calls to the PEC.
  162. HookPublic(i, pec_start + hdr[AMX_HDR_DAT] - hdr[AMX_HDR_COD]);
  163. g_publics[i][ppi_child_time] = 0;
  164. g_publics[i][ppi_total_time] = 0;
  165. g_publics[i][ppi_num_calls] = 0;
  166. g_num_publics++;
  167. }
  168. }
  169. stock bool:ProfilerWriteData(const filename[]) {
  170. new File:file = fopen(filename, io_write);
  171. new buffer[100];
  172. if (!file) {
  173. return false;
  174. }
  175. fwrite(file, "+----------------------------------+----------------+----------------+----------------+\n");
  176. fwrite(file, "| name | calls | self_time | total_time |\n");
  177. fwrite(file, "+----------------------------------+----------------+----------------+----------------+\n");
  178. for (new i = 0; i < g_num_publics; i++) {
  179. new name[32];
  180. GetPublicNameByIndex(i, name);
  181. format(buffer, sizeof(buffer), "| %32s |%15d |%15d |%15d |\n",
  182. name,
  183. g_publics[i][ppi_num_calls],
  184. g_publics[i][ppi_total_time] - g_publics[i][ppi_child_time],
  185. g_publics[i][ppi_total_time]
  186. );
  187. fwrite(file, buffer);
  188. fwrite(file, "+----------------------------------+----------------+----------------+----------------+\n");
  189. }
  190. fclose(file);
  191. return true;
  192. }