about summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorKartik K. Agaram <vc@akkartik.com>2021-11-13 08:16:41 -0800
committerKartik K. Agaram <vc@akkartik.com>2021-11-13 08:16:41 -0800
commit5a44605143362c8d240c20c849593a3739bf9bed (patch)
treea2a866314c30ce299ed90a3ed31ebe526ea286d8
parent0f4faf0e5333542040a809e3b97d05426b69d40d (diff)
downloadteliva-5a44605143362c8d240c20c849593a3739bf9bed.tar.gz
instrumenting function calls with their depth
-rw-r--r--src/Makefile2
-rw-r--r--src/ldebug.c2
-rw-r--r--src/ldo.c54
-rw-r--r--src/lua.c10
4 files changed, 66 insertions, 2 deletions
diff --git a/src/Makefile b/src/Makefile
index c88fb28..39d2c3a 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -8,7 +8,7 @@
 PLAT= none
 
 CC= gcc
-CFLAGS= -O2 -Wall $(MYCFLAGS)
+CFLAGS= -g -O2 -Wall $(MYCFLAGS)
 AR= ar rc
 RANLIB= ranlib
 RM= rm -f
diff --git a/src/ldebug.c b/src/ldebug.c
index 50ad3d3..de643b0 100644
--- a/src/ldebug.c
+++ b/src/ldebug.c
@@ -314,7 +314,7 @@ static int checkArgMode (const Proto *pt, int r, enum OpArgMask mode) {
 }
 
 
-static Instruction symbexec (const Proto *pt, int lastpc, int reg) {
+Instruction symbexec (const Proto *pt, int lastpc, int reg) {
   int pc;
   int last;  /* stores position of last instruction that changed `reg' */
   last = pt->sizecode-1;  /* points to final return (a `neutral' instruction) */
diff --git a/src/ldo.c b/src/ldo.c
index ffcb031..ea186d3 100644
--- a/src/ldo.c
+++ b/src/ldo.c
@@ -6,6 +6,7 @@
 
 
 #include <setjmp.h>
+#include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 
@@ -256,6 +257,58 @@ static StkId tryfuncTM (lua_State *L, StkId func) {
 }
 
 
+/* based on getfuncname */
+extern Instruction symbexec (const Proto *pt, int lastpc, int reg);
+extern int luaL_newmetatable (lua_State *L, const char *tname);
+extern void endwin (void);
+void record_depth_of_global_function (lua_State *L, CallInfo *ci) {
+  if (!isLua(ci))
+    return;
+  if (ci->tailcalls > 0)
+    return;
+  if (!isLua(ci - 1))
+    return;
+  ci--;  /* calling function */
+  if (ci == L->ci)
+    ci->savedpc = L->savedpc;
+  int pc = cast(int, ci->savedpc - ci_func(ci)->l.p->code) - 1;
+  lua_assert(pc != -1);  // TODO: lua_assert not triggering
+  Instruction i = ci_func(ci)->l.p->code[pc];
+//?   endwin();
+//?   printf("AAA: %p %d %d %d\n", L->savedpc, pc, i, GET_OPCODE(i));
+//?   abort();
+  if (GET_OPCODE(i) != OP_CALL && GET_OPCODE(i) != OP_TAILCALL &&
+      GET_OPCODE(i) != OP_TFORLOOP)
+    return;
+  Proto *p = ci_func(ci)->l.p;
+  i = symbexec(p, pc, GETARG_A(i));  /* previous instruction that writes to call's RA */
+  if (GET_OPCODE(i) != OP_GETGLOBAL)
+    return;
+  int g = GETARG_Bx(i);  /* global index */
+  lua_assert(ttisstring(&p->k[g]));
+  const char *name = svalue(&p->k[g]);
+  int depth = ci - L->base_ci;
+  /* Maintain a global table mapping from function name to call-stack depth
+   * at first call to it.
+   *
+   * Won't be perfect; might get confused by shadowing locals. But we can't
+   * be perfect without a bidirectional mapping between interpreter state
+   * and source code. Which would make Lua either a lot less dynamic or a
+   * a lot more like Smalltalk. */
+  // push table
+  luaL_newmetatable(L, "__teliva_call_graph_depth");
+  int cgt = lua_gettop(L);
+  // if key doesn't already exist, set it
+  lua_getfield(L, cgt, name);
+  if (lua_isnil(L, -1)) {
+    lua_pushinteger(L, depth);
+    lua_setfield(L, cgt, name);
+  }
+  // clean up
+  lua_pop(L, 1);  // key
+  lua_pop(L, 1);  // table
+}
+
 
 #define inc_ci(L) \
   ((L->ci == L->end_ci) ? growCI(L) : \
@@ -297,6 +350,7 @@ int luaD_precall (lua_State *L, StkId func, int nresults) {
     for (st = L->top; st < ci->top; st++)
       setnilvalue(st);
     L->top = ci->top;
+    record_depth_of_global_function(L, ci);
     if (L->hookmask & LUA_MASKCALL) {
       L->savedpc++;  /* hooks assume 'pc' is already incremented */
       luaD_callhook(L, LUA_HOOKCALL, -1);
diff --git a/src/lua.c b/src/lua.c
index 08c5f92..59c50ca 100644
--- a/src/lua.c
+++ b/src/lua.c
@@ -370,6 +370,16 @@ void switch_to_editor (lua_State *L, const char *message) {
   if (Script_name)
     edit(L, Script_name, message);
   else {
+    luaL_newmetatable(L, "__teliva_call_graph_depth");
+    int cgt = lua_gettop(L);
+    printf("cgt: %d\n", cgt);
+    for (lua_pushnil(L); lua_next(L, cgt) != 0;) {
+      const char* function_name = lua_tostring(L, -2);
+      int depth = lua_tointeger(L, -1);
+      printf("%s: %d\n", function_name, depth);
+      lua_pop(L, 1);  // pop value, leave key on stack for next iteration
+    }
+    exit(4);
     Current_definition = "main";
     write_definition_to_file(L, Current_definition, "teliva_editbuffer");
     edit(L, "teliva_editbuffer", /*status message*/ "");