introspect-debug-info.inc 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454
  1. #include <a_samp>
  2. #if !defined INTROSPECT_MAX_ARGS
  3. #define INTROSPECT_MAX_ARGS 16
  4. #endif
  5. #if !defined INTROSPECT_MAX_GLOBALS
  6. #define INTROSPECT_MAX_GLOBALS 2048
  7. #endif
  8. #if !defined INTROSPECT_MAX_FUNCTIONS
  9. #define INTROSPECT_MAX_FUNCTIONS 2048
  10. #endif
  11. #if !defined INTROSPECT_MAX_TOTAL_ARGS
  12. #define INTROSPECT_MAX_TOTAL_ARGS (INTROSPECT_MAX_FUNCTIONS * 4)
  13. #endif
  14. #if !defined INTROSPECT_MAX_TAGS
  15. #define INTROSPECT_MAX_TAGS 512
  16. #endif
  17. enum {
  18. IDENT_VARIABLE = 1,
  19. IDENT_REFERENCE = 2,
  20. IDENT_ARRAY = 3,
  21. IDENT_REFARRAY = 4,
  22. IDENT_FUNCTION = 9
  23. }
  24. enum {
  25. VCLASS_GLOBAL,
  26. VCLASS_LOCAL,
  27. VCLASS_STATIC
  28. }
  29. enum E_VARIABLE {
  30. Address,
  31. Tag,
  32. CodeStart,
  33. CodeEnd,
  34. Ident,
  35. Name[32],
  36. Dimensions,
  37. DimensionSize[3],
  38. DimensionTag[3]
  39. }
  40. enum E_FUNCTION {
  41. Address,
  42. CodeStart = 0,
  43. CodeEnd,
  44. Name[48],
  45. Tag,
  46. DimensionSize,
  47. StaticArgs,
  48. Arg[INTROSPECT_MAX_ARGS]
  49. }
  50. enum E_TAG {
  51. Tag,
  52. Name[32]
  53. }
  54. static stock
  55. s_Globals[INTROSPECT_MAX_GLOBALS][E_VARIABLE],
  56. s_Arguments[INTROSPECT_MAX_TOTAL_ARGS][E_VARIABLE],
  57. s_Functions[INTROSPECT_MAX_FUNCTIONS][E_FUNCTION],
  58. s_Tags[INTROSPECT_MAX_TAGS][E_TAG],
  59. s_NumGlobals,
  60. s_NumArguments,
  61. s_NumFunctions,
  62. s_NumTags
  63. ;
  64. public _@_isdi_funcinc();
  65. public _@_isdi_funcinc() {
  66. strunpack("", "", 0);
  67. }
  68. stock ReadAmxDebugData(File:fp) {
  69. new offset = 0;
  70. s_NumGlobals = 0;
  71. s_NumArguments = 0;
  72. s_NumFunctions = 0;
  73. s_NumTags = 0;
  74. // Go to the end of the AMX (0 contains the size excluding debug information)
  75. offset = freadcell(fp);
  76. fseek(fp, 0), fseek(fp, offset);
  77. // Read the header
  78. new size = freadcell(fp);
  79. new magic = freadshort(fp);
  80. new file_version = freadbyte(fp);
  81. new amx_version = freadbyte(fp);
  82. new flags = freadshort(fp);
  83. new num_files = freadshort(fp);
  84. new num_lines = freadshort(fp);
  85. new num_symbols = freadshort(fp);
  86. new num_tags = freadshort(fp);
  87. new num_machines = freadshort(fp);
  88. new num_states = freadshort(fp);
  89. offset += 22;
  90. #pragma unused num_states, num_machines, flags, amx_version, file_version, magic, size
  91. // Skip the file table
  92. for (new i = 0; i < num_files; i++) {
  93. fseek(fp, 4, seek_current);
  94. offset += 4;
  95. do {
  96. offset++;
  97. } while (freadbyte(fp));
  98. }
  99. // Skip the line table
  100. fseek(fp, num_lines * 8, seek_current);
  101. offset += num_lines * 8;
  102. // Read global variables and functions from the symbol table
  103. ReadSymbolTable(fp, num_symbols);
  104. // Read function arguments from the symbol table
  105. fseek(fp, offset);
  106. ReadSymbolTable(fp, num_symbols, true);
  107. // Read tags
  108. for (new i = 0; i < num_tags; i++) {
  109. s_Tags[i][Tag] = freadshort(fp);
  110. freadstr(fp, s_Tags[i][Name], 32);
  111. }
  112. s_NumTags = num_tags;
  113. }
  114. static stock ReadSymbolTable(File:fp, num_symbols, bool:read_args = false) {
  115. for (new i = 0; i < num_symbols; i++) {
  116. new address = freadcell(fp);
  117. new tag = freadshort(fp);
  118. new codestart = freadcell(fp);
  119. new codeend = freadcell(fp);
  120. new ident = freadbyte(fp);
  121. new vclass = freadbyte(fp);
  122. new dim = freadshort(fp);
  123. new name[48]; freadstr(fp, name);
  124. new dim_tag[4], dim_size[4];
  125. for (new j = 0; j < dim; j++) {
  126. dim_tag[j] = freadshort(fp);
  127. dim_size[j] = freadcell(fp);
  128. }
  129. if (read_args) {
  130. if (vclass == VCLASS_LOCAL) {
  131. for (new n = 0; n < s_NumFunctions; n++) {
  132. if (s_Functions[n][CodeStart] == codestart) {
  133. s_Arguments[s_NumArguments][Address] = address;
  134. s_Arguments[s_NumArguments][Tag] = tag;
  135. s_Arguments[s_NumArguments][Dimensions] = dim;
  136. s_Arguments[s_NumArguments][CodeStart] = codeend;
  137. s_Arguments[s_NumArguments][CodeEnd] = codestart;
  138. s_Arguments[s_NumArguments][Ident] = ident;
  139. strunpack(s_Arguments[s_NumArguments][Name], name, 32);
  140. for (new j = 0; j < 3; j++) {
  141. s_Arguments[s_NumArguments][DimensionTag][j] = dim_tag[j];
  142. s_Arguments[s_NumArguments][DimensionSize][j] = dim_size[j];
  143. }
  144. s_Functions[n][Arg][s_Functions[n][StaticArgs]] = s_NumArguments;
  145. s_Functions[n][StaticArgs]++;
  146. s_NumArguments++;
  147. break;
  148. }
  149. }
  150. }
  151. continue;
  152. }
  153. if (vclass != VCLASS_GLOBAL) {
  154. continue;
  155. }
  156. switch (ident) {
  157. case IDENT_VARIABLE,
  158. IDENT_ARRAY: {
  159. // TODO: detect if it's a global static
  160. s_Globals[s_NumGlobals][Address] = address;
  161. s_Globals[s_NumGlobals][Tag] = tag;
  162. s_Globals[s_NumGlobals][Dimensions] = dim;
  163. s_Globals[s_NumGlobals][CodeStart] = codeend;
  164. s_Globals[s_NumGlobals][CodeEnd] = codestart;
  165. s_Globals[s_NumGlobals][Ident] = ident;
  166. strunpack(s_Globals[s_NumGlobals][Name], name, 32);
  167. for (new j = 0; j < 3; j++) {
  168. s_Globals[s_NumGlobals][DimensionTag][j] = dim_tag[j];
  169. s_Globals[s_NumGlobals][DimensionSize][j] = dim_size[j];
  170. }
  171. s_NumGlobals++;
  172. }
  173. case IDENT_FUNCTION: {
  174. s_Functions[s_NumFunctions][Address] = address;
  175. s_Functions[s_NumFunctions][CodeEnd] = codeend;
  176. strunpack(s_Functions[s_NumFunctions][Name], name, 48);
  177. s_Functions[s_NumFunctions][Tag] = tag;
  178. s_Functions[s_NumFunctions][DimensionSize] = dim_size[0];
  179. s_NumFunctions++;
  180. }
  181. }
  182. }
  183. }
  184. stock GetFunctionFromAddress(address) {
  185. new ret[48];
  186. for (new i = 0; i < s_NumFunctions; i++) {
  187. if (address == s_Functions[i][Address]) {
  188. strcat(ret, s_Functions[i][Name]);
  189. break;
  190. }
  191. }
  192. return ret;
  193. }
  194. stock GetVariableFromAddress(address) {
  195. new ret[32];
  196. for (new i = 0; i < s_NumGlobals; i++) {
  197. if (address == s_Globals[i][Address]) {
  198. strcat(ret, s_Globals[i][Name]);
  199. break;
  200. }
  201. }
  202. return ret;
  203. }
  204. stock GetFunctionIdx(const name[]) {
  205. for (new i = 0; i < s_NumFunctions; i++) {
  206. if (!strcmp(name, s_Functions[i][Name])) {
  207. return i;
  208. }
  209. }
  210. return -1;
  211. }
  212. stock GetVariableIdx(const name[]) {
  213. for (new i = 0; i < s_NumGlobals; i++) {
  214. if (!strcmp(name, s_Globals[i][Name])) {
  215. return i;
  216. }
  217. }
  218. return -1;
  219. }
  220. stock GetFunctionInfo(const name[], info[E_FUNCTION]) {
  221. new i = GetFunctionIdx(name);
  222. if (i == -1) {
  223. return false;
  224. }
  225. memcpy(info[E_FUNCTION:0], s_Functions[i][E_FUNCTION:0], 0, _:E_FUNCTION * 4, _:E_FUNCTION);
  226. return true;
  227. }
  228. stock GetVariableInfo(const name[], info[E_VARIABLE]) {
  229. new i = GetVariableIdx(name);
  230. if (i == -1) {
  231. return false;
  232. }
  233. memcpy(info[E_VARIABLE:0], s_Globals[i][E_VARIABLE:0], 0, _:E_VARIABLE * 4, _:E_VARIABLE);
  234. return true;
  235. }
  236. stock GetFunctionArgument(const func_info[E_FUNCTION], idx, info[E_VARIABLE]) {
  237. idx = func_info[StaticArgs] - 1 - idx;
  238. if (idx < 0 || idx >= INTROSPECT_MAX_ARGS) {
  239. return false;
  240. }
  241. new arg = func_info[Arg][idx];
  242. if (!(0 <= arg < s_NumArguments)) {
  243. return false;
  244. }
  245. memcpy(info[E_VARIABLE:0], s_Arguments[arg][E_VARIABLE:0], 0, _:E_VARIABLE * 4, _:E_VARIABLE);
  246. return true;
  247. }
  248. stock GetTagIdx(const name[]) {
  249. for (new i = 0; i < s_NumTags; i++) {
  250. if (!strcmp(name, s_Tags[i][Name])) {
  251. return s_Tags[i][Tag] | 0xC0000000;
  252. }
  253. }
  254. return -1;
  255. }
  256. stock GetTagName(tag) {
  257. new output[32] = "";
  258. tag &= 0xC0000000;
  259. for (new i = 0; i < s_NumTags; i++) {
  260. if (s_Tags[i][Tag] == tag) {
  261. strunpack(output, s_Tags[i][Name]);
  262. }
  263. }
  264. return output;
  265. }
  266. stock GetStringFromVariableValue(output[], maxlength = sizeof(output), const info[E_VARIABLE], ...) {
  267. if (info[Dimensions] > 0) {
  268. if (info[Dimensions] > 1 || (info[Tag] && info[DimensionSize] == 0)) {
  269. if (info[Tag]) {
  270. new tag[32]; tag = GetTagName(info[Tag]);
  271. format(output, maxlength, "<%s:array", tag);
  272. } else {
  273. strunpack(output, "<array", maxlength);
  274. }
  275. for (new i = 0; i < info[Dimensions]; i++) {
  276. if (info[DimensionSize][i]) {
  277. format(output, maxlength, "%s[%d]", output, info[DimensionSize][i]);
  278. } else {
  279. strcat(output, "[]", maxlength);
  280. }
  281. }
  282. strcat(output, ">", maxlength);
  283. return;
  284. } else {
  285. // TODO: is zero-terminated with mostly ascii characters? otherwise array
  286. if (info[Tag]) {
  287. new tag[32]; tag = GetTagName(info[Tag]);
  288. strunpack(output, "[", maxlength);
  289. if (!strcmp("Float", tag)) {
  290. for (new i = 0; i < info[DimensionSize][0]; i++) {
  291. format(output, maxlength, i ? ("%s, %.4f") : ("%s%.4f"), output, getarg(3, i));
  292. }
  293. } else {
  294. for (new i = 0; i < info[DimensionSize][0]; i++) {
  295. format(output, maxlength, i ? ("%s, %d") : ("%s%d"), output, getarg(3, i));
  296. }
  297. }
  298. strcat(output, "]", maxlength);
  299. } else {
  300. output[0] = '\0';
  301. // TODO: string literal
  302. #emit PUSH.S maxlength
  303. #emit LCTRL 5
  304. #emit ADD.C 24
  305. #emit LOAD.I
  306. #emit PUSH.pri
  307. #emit PUSH.S output
  308. #emit PUSH.C 12
  309. #emit SYSREQ.C strunpack
  310. #emit STACK 16
  311. }
  312. }
  313. } else {
  314. if (!info[Tag]) {
  315. format(output, maxlength, "%d", getarg(3));
  316. } else {
  317. new tag[32]; tag = GetTagName(info[Tag]);
  318. if (!strcmp("Float", tag)) {
  319. format(output, maxlength, "%.4f", getarg(3));
  320. } else if (!strcmp("bool", tag, true)) {
  321. if (getarg(3)) {
  322. strunpack(output, "true", maxlength);
  323. } else {
  324. strunpack(output, "false", maxlength);
  325. }
  326. } else if (!strcmp("hex", tag, true)) {
  327. format(output, maxlength, "%08x", getarg(3));
  328. } else if (!strcmp("bin", tag, true) || !strcmp("binary", tag, true)) {
  329. format(output, maxlength, "%032b", getarg(3));
  330. }
  331. }
  332. }
  333. }
  334. // Read a single cell
  335. static stock freadcell(File:fp) {
  336. new buf[1];
  337. fblockread(fp, buf);
  338. return buf[0];
  339. }
  340. // Read a 16-bit integer
  341. static stock freadshort(File:fp) {
  342. new buf[1];
  343. fblockread(fp, buf);
  344. fseek(fp, -2, seek_current);
  345. return buf[0] & 0xFFFF;
  346. }
  347. // Read a single byte
  348. static stock freadbyte(File:fp) {
  349. return fgetchar(fp, 0, false);
  350. }
  351. // Read a zero-terminated string
  352. static stock freadstr(File:fp, buf[], maxlength = sizeof(buf)) {
  353. new i = 0, c;
  354. buf[i] = '\0';
  355. while ((c = fgetchar(fp, 0))) {
  356. if (i + 1 < maxlength) {
  357. buf[i++] = c;
  358. }
  359. }
  360. buf[i] = '\0';
  361. return i;
  362. }