[Tarantool-patches] [PATCH luajit v2] memprof: enrich symtab when new trace is allocated

Mikhail Shishatskiy m.shishatskiy at tarantool.org
Wed Dec 8 20:22:07 MSK 2021


Since symtab can be enriched with new prototypes, it
can be enriched with new traces as well. This feature
can help to investigate trace-generation-heavy apps
with memprof.

This patch introduces the functionality described above by
adding a `prof_epoch` counter to the `GCtrace` structure.
If the profiler meets allocation from a trace, it checks if
the trace was already streamed to the symtab. If not,
a symtab entry is dumped inside an <event-symtab>:

| event-symtab := event-header sym?
| sym := sym-lua | sym-trace
| sym-trace := trace-no sym-addr line-no

Since this patch, traces are no longer identified by address,
as all the name collisions are resolved with symbols epochs.

Also, the API of <utils/symtab.lua> changed: function `parse_sym_trace`
is now public in order to use it from the <memrprof/parser.lua> module.

Follows up tarantool/tarantool#5815
---

CI: https://github.com/tarantool/tarantool/tree/shishqa/enrich-symtab-when-trace-is-allocated-v2
branch: https://github.com/tarantool/luajit/tree/shishqa/enrich-symtab-when-trace-is-allocated-v2

 src/lj_jit.h                                  |  8 +++++++
 src/lj_memprof.c                              | 22 +++++++++++++------
 src/lj_memprof.h                              |  5 +++--
 src/lj_trace.c                                |  3 +++
 .../misclib-memprof-lapi.test.lua             | 13 +++++------
 tools/memprof/parse.lua                       |  3 ++-
 tools/utils/symtab.lua                        | 14 ++++--------
 7 files changed, 41 insertions(+), 27 deletions(-)

diff --git a/src/lj_jit.h b/src/lj_jit.h
index d82292f8..766892aa 100644
--- a/src/lj_jit.h
+++ b/src/lj_jit.h
@@ -254,7 +254,15 @@ typedef struct GCtrace {
   uint8_t sinktags;	/* Trace has SINK tags. */
   uint8_t topslot;	/* Top stack slot already checked to be allocated. */
   uint8_t linktype;	/* Type of link. */
+#if LJ_HASMEMPROF
+  /*
+  ** Epoch indicating if this trace was dumped to the symbol table for the
+  ** current profiling session.
+  */
+  uint8_t prof_epoch;
+#else
   uint8_t unused1;
+#endif
 #ifdef LUAJIT_USE_GDBJIT
   void *gdbjit_entry;	/* GDB JIT entry. */
 #endif
diff --git a/src/lj_memprof.c b/src/lj_memprof.c
index c154de93..367fbe2b 100644
--- a/src/lj_memprof.c
+++ b/src/lj_memprof.c
@@ -30,7 +30,8 @@ static const unsigned char ljs_header[] = {'l', 'j', 's', LJS_CURRENT_VERSION,
 
 #if LJ_HASJIT
 
-static void dump_symtab_trace(struct lj_wbuf *out, const GCtrace *trace)
+static void dump_symtab_trace(struct lj_wbuf *out, GCtrace *trace,
+			      const global_State *g)
 {
   GCproto *pt = &gcref(trace->startpt)->pt;
   BCLine lineno = 0;
@@ -41,9 +42,7 @@ static void dump_symtab_trace(struct lj_wbuf *out, const GCtrace *trace)
 
   lineno = lj_debug_line(pt, proto_bcpos(pt, startpc));
 
-  lj_wbuf_addbyte(out, SYMTAB_TRACE);
   lj_wbuf_addu64(out, (uint64_t)trace->traceno);
-  lj_wbuf_addu64(out, (uint64_t)trace->mcode);
   /*
   ** The information about the prototype, associated with the
   ** trace's start has already been dumped, as it is anchored
@@ -53,11 +52,14 @@ static void dump_symtab_trace(struct lj_wbuf *out, const GCtrace *trace)
   */
   lj_wbuf_addu64(out, (uintptr_t)pt);
   lj_wbuf_addu64(out, (uint64_t)lineno);
+
+  trace->prof_epoch = g->prof_epoch;
 }
 
 #else
 
-static void dump_symtab_trace(struct lj_wbuf *out, const GCtrace *trace)
+static void dump_symtab_trace(struct lj_wbuf *out, const GCtrace *trace,
+			      const global_State *g)
 {
   UNUSED(out);
   UNUSED(trace);
@@ -93,7 +95,8 @@ static void dump_symtab(struct lj_wbuf *out, const struct global_State *g)
       break;
     }
     case (~LJ_TTRACE): {
-      dump_symtab_trace(out, gco2trace(o));
+      lj_wbuf_addbyte(out, SYMTAB_TRACE);
+      dump_symtab_trace(out, gco2trace(o), g);
       break;
     }
     default:
@@ -222,10 +225,15 @@ static void memprof_write_trace(struct memprof *mp, uint8_t aevent)
   const global_State *g = mp->g;
   const jit_State *J = G2J(g);
   const TraceNo traceno = g->vmstate;
-  const GCtrace *trace = traceref(J, traceno);
+  GCtrace *trace = traceref(J, traceno);
+
+  if (LJ_UNLIKELY(trace->prof_epoch != g->prof_epoch)) {
+    lj_wbuf_addbyte(out, AEVENT_SYMTAB | ASOURCE_TRACE);
+    dump_symtab_trace(out, trace, g);
+  }
+
   lj_wbuf_addbyte(out, aevent | ASOURCE_TRACE);
   lj_wbuf_addu64(out, (uint64_t)traceno);
-  lj_wbuf_addu64(out, (uintptr_t)trace->mcode);
 }
 
 #else
diff --git a/src/lj_memprof.h b/src/lj_memprof.h
index 150e6b32..0b06eb5b 100644
--- a/src/lj_memprof.h
+++ b/src/lj_memprof.h
@@ -27,7 +27,7 @@
 ** reserved       := <BYTE> <BYTE> <BYTE>
 ** sym            := sym-lua | sym-trace | sym-final
 ** sym-lua        := sym-header sym-addr sym-chunk sym-line
-** sym-trace      := sym-header trace-no trace-addr sym-addr sym-line
+** sym-trace      := sym-header trace-no sym-addr sym-line
 ** sym-header     := <BYTE>
 ** sym-addr       := <ULEB128>
 ** sym-chunk      := string
@@ -78,8 +78,9 @@
 ** loc-lua        := sym-addr line-no
 ** loc-c          := sym-addr
 ** loc-trace      := trace-no trace-addr
-** sym            := sym-lua
+** sym            := sym-lua | sym-trace
 ** sym-lua        := sym-addr sym-chunk sym-line
+** sym-trace      := trace-no sym-addr line-no
 ** sym-addr       := <ULEB128>
 ** sym-chunk      := string
 ** sym-line       := <ULEB128>
diff --git a/src/lj_trace.c b/src/lj_trace.c
index 86563cdb..3fc6fc14 100644
--- a/src/lj_trace.c
+++ b/src/lj_trace.c
@@ -163,6 +163,9 @@ static void trace_save(jit_State *J, GCtrace *T)
 #ifdef LUAJIT_USE_PERFTOOLS
   perftools_addtrace(T);
 #endif
+#if LJ_HASMEMPROF
+  T->prof_epoch = 0;
+#endif
 }
 
 void LJ_FASTCALL lj_trace_free(global_State *g, GCtrace *T)
diff --git a/test/tarantool-tests/misclib-memprof-lapi.test.lua b/test/tarantool-tests/misclib-memprof-lapi.test.lua
index 1c74b4d7..b7822563 100644
--- a/test/tarantool-tests/misclib-memprof-lapi.test.lua
+++ b/test/tarantool-tests/misclib-memprof-lapi.test.lua
@@ -267,7 +267,7 @@ test:test("jit-output", function(subtest)
     return
   end
 
-  subtest:plan(3)
+  subtest:plan(4)
 
   jit.opt.start(3, "hotloop=10")
   jit.flush()
@@ -282,13 +282,12 @@ test:test("jit-output", function(subtest)
   -- See also https://github.com/tarantool/tarantool/issues/5679.
   subtest:ok(alloc[0] == nil)
 
-  -- Run already generated traces.
-  symbols, events = generate_parsed_output(default_payload)
-
-  alloc = fill_ev_type(events, symbols, "alloc")
-
   -- We expect, that loop will be compiled into a trace.
-  subtest:ok(check_alloc_report(alloc, { traceno = 1, line = 37 }, 20))
+  -- 10 allocations in interpreter mode, 1 allocation for a trace
+  -- recording and assembling and next 9 allocations will happen
+  -- inside the trace.
+  subtest:ok(check_alloc_report(alloc, { line = 39, linedefined = 32 }, 11))
+  subtest:ok(check_alloc_report(alloc, { traceno = 1, line = 37 }, 9))
   -- See same checks with jit.off().
   subtest:ok(check_alloc_report(alloc, { line = 34, linedefined = 32 }, 2))
 
diff --git a/tools/memprof/parse.lua b/tools/memprof/parse.lua
index 38f76f00..fb4fcf3c 100644
--- a/tools/memprof/parse.lua
+++ b/tools/memprof/parse.lua
@@ -77,7 +77,6 @@ local function parse_location(reader, asource, symbols)
     line = reader:read_uleb128()
   elseif asource == ASOURCE_TRACE then
     traceno = reader:read_uleb128()
-    addr = reader:read_uleb128()
   else
     error("Unknown asource "..asource)
   end
@@ -144,6 +143,8 @@ end
 local function parse_symtab(reader, asource, _, _, symbols)
   if asource == ASOURCE_LFUNC then
     symtab.parse_sym_lfunc(reader, symbols)
+  elseif asource == ASOURCE_TRACE then
+    symtab.parse_sym_trace(reader, symbols)
   end
 end
 
diff --git a/tools/utils/symtab.lua b/tools/utils/symtab.lua
index 133a0fc7..fced4c20 100644
--- a/tools/utils/symtab.lua
+++ b/tools/utils/symtab.lua
@@ -59,9 +59,8 @@ function M.parse_sym_lfunc(reader, symtab)
   })
 end
 
-local function parse_sym_trace(reader, symtab)
+function M.parse_sym_trace(reader, symtab)
   local traceno = reader:read_uleb128()
-  local trace_addr = reader:read_uleb128()
   local sym_addr = reader:read_uleb128()
   local sym_line = reader:read_uleb128()
 
@@ -70,14 +69,13 @@ local function parse_sym_trace(reader, symtab)
   end
 
   table.insert(symtab.trace[traceno], {
-    addr = trace_addr,
     start = M.new_loc(symtab, sym_addr, sym_line, 0)
   })
 end
 
 local parsers = {
   [SYMTAB_LFUNC] = M.parse_sym_lfunc,
-  [SYMTAB_TRACE] = parse_sym_trace,
+  [SYMTAB_TRACE] = M.parse_sym_trace,
 }
 
 function M.parse(reader)
@@ -131,18 +129,14 @@ end
 
 local function demangle_trace(symtab, loc)
   local traceno = loc.traceno
-  local addr = loc.addr
 
   assert(traceno ~= 0, "Location is a trace")
 
-  local trace_str = string_format("TRACE [%d] %#x", traceno, addr)
+  local trace_str = string_format("TRACE [%d]", traceno)
   local epochs = symtab.trace[traceno]
   local trace = epochs and epochs[loc.epoch]
 
-  -- If trace, which was remembered in the symtab, has not
-  -- been flushed, associate it with a proto, where trace
-  -- recording started.
-  if trace and trace.addr == addr then
+  if trace then
     assert(trace.start.traceno == 0, "Trace start is not a trace")
     return trace_str.." started at "..M.demangle(symtab, trace.start)
   end
-- 
2.33.1



More information about the Tarantool-patches mailing list