about summary refs log tree commit diff stats
path: root/src/ldo.c
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 /src/ldo.c
parent0f4faf0e5333542040a809e3b97d05426b69d40d (diff)
downloadteliva-5a44605143362c8d240c20c849593a3739bf9bed.tar.gz
instrumenting function calls with their depth
Diffstat (limited to 'src/ldo.c')
-rw-r--r--src/ldo.c54
1 files changed, 54 insertions, 0 deletions
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);