perf.c 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147
  1. //
  2. // This module allows performance monitoring by looking at
  3. // the PC at regular intervals and building a histogram
  4. //
  5. // perf.start(start, end, nbins[, pc offset on stack])
  6. // perf.stop() -> total sample, samples outside range, table { addr -> count , .. }
  7. #include "ets_sys.h"
  8. #include "os_type.h"
  9. #include "osapi.h"
  10. #include "c_stdlib.h"
  11. #include "module.h"
  12. #include "lauxlib.h"
  13. #include "platform.h"
  14. #include "hw_timer.h"
  15. #include "cpu_esp8266.h"
  16. typedef struct {
  17. int ref;
  18. uint32_t start;
  19. uint32_t bucket_shift;
  20. uint32_t bucket_count;
  21. uint32_t total_samples;
  22. uint32_t outside_samples;
  23. uint32_t pc_offset;
  24. uint32_t bucket[1];
  25. } DATA;
  26. static DATA *data;
  27. extern char _flash_used_end[];
  28. #define TIMER_OWNER ((os_param_t) 'p')
  29. static void ICACHE_RAM_ATTR hw_timer_cb(os_param_t p)
  30. {
  31. (void) p;
  32. uint32_t stackaddr;
  33. if (data) {
  34. uint32_t pc = *(&stackaddr + data->pc_offset);
  35. uint32_t bucket_number = (pc - data->start) >> data->bucket_shift;
  36. if (bucket_number < data->bucket_count) {
  37. data->bucket[bucket_number]++;
  38. } else {
  39. data->outside_samples++;
  40. }
  41. data->total_samples++;
  42. }
  43. }
  44. static int perf_start(lua_State *L)
  45. {
  46. uint32_t start = luaL_optinteger(L, 1, 0x40000000);
  47. uint32_t end = luaL_optinteger(L, 2, (uint32_t) _flash_used_end);
  48. uint32_t bins = luaL_optinteger(L, 3, 1024);
  49. if (end <= start) {
  50. luaL_error(L, "end must be larger than start");
  51. }
  52. uint32_t binsize = (end - start + bins - 1) / bins;
  53. // Round up to a power of two
  54. int shift;
  55. binsize = binsize - 1;
  56. for (shift = 0; binsize > 0; shift++) {
  57. binsize >>= 1;
  58. }
  59. bins = (end - start + (1 << shift) - 1) / (1 << shift);
  60. int pc_offset = 20; // This appears to be correct
  61. if (lua_gettop(L) >= 4) {
  62. pc_offset = luaL_checkinteger(L, 4);
  63. }
  64. size_t data_size = sizeof(DATA) + bins * sizeof(uint32_t);
  65. DATA *d = (DATA *) lua_newuserdata(L, data_size);
  66. memset(d, 0, data_size);
  67. d->ref = luaL_ref(L, LUA_REGISTRYINDEX);
  68. d->start = start;
  69. d->bucket_shift = shift;
  70. d->bucket_count = bins;
  71. d->pc_offset = pc_offset;
  72. if (data) {
  73. lua_unref(L, data->ref);
  74. }
  75. data = d;
  76. // Start the timer
  77. if (!platform_hw_timer_init(TIMER_OWNER, FRC1_SOURCE, TRUE)) {
  78. // Failed to init the timer
  79. data = NULL;
  80. lua_unref(L, d->ref);
  81. luaL_error(L, "Unable to initialize timer");
  82. }
  83. platform_hw_timer_set_func(TIMER_OWNER, hw_timer_cb, 0);
  84. platform_hw_timer_arm_us(TIMER_OWNER, 50);
  85. return 0;
  86. }
  87. static int perf_stop(lua_State *L)
  88. {
  89. if (!data) {
  90. return 0;
  91. }
  92. // stop the timer
  93. platform_hw_timer_close(TIMER_OWNER);
  94. DATA *d = data;
  95. data = NULL;
  96. lua_pushnumber(L, d->total_samples);
  97. lua_pushnumber(L, d->outside_samples);
  98. lua_newtable(L);
  99. int i;
  100. uint32_t addr = d->start;
  101. for (i = 0; i < d->bucket_count; i++, addr += (1 << d->bucket_shift)) {
  102. if (d->bucket[i]) {
  103. lua_pushnumber(L, addr);
  104. lua_pushnumber(L, d->bucket[i]);
  105. lua_settable(L, -3);
  106. }
  107. }
  108. lua_pushnumber(L, 1 << d->bucket_shift);
  109. lua_unref(L, d->ref);
  110. return 4;
  111. }
  112. static const LUA_REG_TYPE perf_map[] = {
  113. { LSTRKEY( "start" ), LFUNCVAL( perf_start ) },
  114. { LSTRKEY( "stop" ), LFUNCVAL( perf_stop ) },
  115. { LNILKEY, LNILVAL }
  116. };
  117. NODEMCU_MODULE(PERF, "perf", perf_map, NULL);