const char parse_lua[] =
"-- Parser of LuaJIT's memprof binary stream.\n"
"-- The format spec can be found in <src/lj_memprof.h>.\n"
"--\n"
"-- Major portions taken verbatim or adapted from the LuaVela.\n"
"-- Copyright (C) 2015-2019 IPONWEB Ltd.\n"
"\n"
"local bit = require \"bit\"\n"
"local band = bit.band\n"
"local lshift = bit.lshift\n"
"\n"
"local string_format = string.format\n"
"\n"
"local LJM_MAGIC = \"ljm\"\n"
"local LJM_CURRENT_VERSION = 1\n"
"\n"
"local LJM_EPILOGUE_HEADER = 0x80\n"
"\n"
"local AEVENT_ALLOC = 1\n"
"local AEVENT_FREE = 2\n"
"local AEVENT_REALLOC = 3\n"
"\n"
"local AEVENT_MASK = 0x3\n"
"\n"
"local ASOURCE_INT = lshift(1, 2)\n"
"local ASOURCE_LFUNC = lshift(2, 2)\n"
"local ASOURCE_CFUNC = lshift(3, 2)\n"
"\n"
"local ASOURCE_MASK = lshift(0x3, 2)\n"
"\n"
"local M = {}\n"
"\n"
"local function new_event(loc)\n"
"  return {\n"
"    loc = loc,\n"
"    num = 0,\n"
"    free = 0,\n"
"    alloc = 0,\n"
"    primary = {},\n"
"  }\n"
"end\n"
"\n"
"local function link_to_previous(heap_chunk, e)\n"
"  -- Memory at this chunk was allocated before we start tracking.\n"
"  if heap_chunk then\n"
"    -- Save Lua code location (line) by address (id).\n"
"    e.primary[heap_chunk[2]] = heap_chunk[3]\n"
"  end\n"
"end\n"
"\n"
"local function id_location(addr, line)\n"
"  return string_format(\"f%#xl%d\", addr, line), {\n"
"    addr = addr,\n"
"    line = line,\n"
"  }\n"
"end\n"
"\n"
"local function parse_location(reader, asource)\n"
"  if asource == ASOURCE_INT then\n"
"    return id_location(0, 0)\n"
"  elseif asource == ASOURCE_CFUNC then\n"
"    return id_location(reader:read_uleb128(), 0)\n"
"  elseif asource == ASOURCE_LFUNC then\n"
"    return id_location(reader:read_uleb128(), reader:read_uleb128())\n"
"  end\n"
"  error(\"Unknown asource \"..asource)\n"
"end\n"
"\n"
"local function parse_alloc(reader, asource, events, heap)\n"
"  local id, loc = parse_location(reader, asource)\n"
"\n"
"  local naddr = reader:read_uleb128()\n"
"  local nsize = reader:read_uleb128()\n"
"\n"
"  if not events[id] then\n"
"    events[id] = new_event(loc)\n"
"  end\n"
"  local e = events[id]\n"
"  e.num = e.num + 1\n"
"  e.alloc = e.alloc + nsize\n"
"\n"
"  heap[naddr] = {nsize, id, loc}\n"
"end\n"
"\n"
"local function parse_realloc(reader, asource, events, heap)\n"
"  local id, loc = parse_location(reader, asource)\n"
"\n"
"  local oaddr = reader:read_uleb128()\n"
"  local osize = reader:read_uleb128()\n"
"  local naddr = reader:read_uleb128()\n"
"  local nsize = reader:read_uleb128()\n"
"\n"
"  if not events[id] then\n"
"    events[id] = new_event(loc)\n"
"  end\n"
"  local e = events[id]\n"
"  e.num = e.num + 1\n"
"  e.free = e.free + osize\n"
"  e.alloc = e.alloc + nsize\n"
"\n"
"  link_to_previous(heap[oaddr], e)\n"
"\n"
"  heap[oaddr] = nil\n"
"  heap[naddr] = {nsize, id, loc}\n"
"end\n"
"\n"
"local function parse_free(reader, asource, events, heap)\n"
"  local id, loc = parse_location(reader, asource)\n"
"\n"
"  local oaddr = reader:read_uleb128()\n"
"  local osize = reader:read_uleb128()\n"
"\n"
"  if not events[id] then\n"
"    events[id] = new_event(loc)\n"
"  end\n"
"  local e = events[id]\n"
"  e.num = e.num + 1\n"
"  e.free = e.free + osize\n"
"\n"
"  link_to_previous(heap[oaddr], e)\n"
"\n"
"  heap[oaddr] = nil\n"
"end\n"
"\n"
"local parsers = {\n"
"  [AEVENT_ALLOC] = {evname = \"alloc\", parse = parse_alloc},\n"
"  [AEVENT_FREE] = {evname = \"free\", parse = parse_free},\n"
"  [AEVENT_REALLOC] = {evname = \"realloc\", parse = parse_realloc},\n"
"}\n"
"\n"
"local function ev_header_is_valid(evh)\n"
"  return evh <= 0x0f or evh == LJM_EPILOGUE_HEADER\n"
"end\n"
"\n"
"-- Splits event header into event type (aka aevent = allocation\n"
"-- event) and event source (aka asource = allocation source).\n"
"local function ev_header_split(evh)\n"
"  return band(evh, AEVENT_MASK), band(evh, ASOURCE_MASK)\n"
"end\n"
"\n"
"local function parse_event(reader, events)\n"
"  local ev_header = reader:read_octet()\n"
"\n"
"  assert(ev_header_is_valid(ev_header), \"Bad ev_header \"..ev_header)\n"
"\n"
"  if ev_header == LJM_EPILOGUE_HEADER then\n"
"    return false\n"
"  end\n"
"\n"
"  local aevent, asource = ev_header_split(ev_header)\n"
"  local parser = parsers[aevent]\n"
"\n"
"  assert(parser, \"Bad aevent \"..aevent)\n"
"\n"
"  parser.parse(reader, asource, events[parser.evname], events.heap)\n"
"\n"
"  return true\n"
"end\n"
"\n"
"function M.parse(reader)\n"
"  local events = {\n"
"    alloc = {},\n"
"    realloc = {},\n"
"    free = {},\n"
"    heap = {},\n"
"  }\n"
"\n"
"  local magic = reader:read_octets(3)\n"
"  local version = reader:read_octets(1)\n"
"  -- Dummy-consume reserved bytes.\n"
"  local _ = reader:read_octets(3)\n"
"\n"
"  if magic ~= LJM_MAGIC then\n"
"    error(\"Bad LJM format prologue: \"..magic)\n"
"  end\n"
"\n"
"  if string.byte(version) ~= LJM_CURRENT_VERSION then\n"
"    error(string_format(\n"
"      \"LJM format version mismatch: the tool expects %d, but your data is %d\",\n"
"      LJM_CURRENT_VERSION,\n"
"      string.byte(version)\n"
"    ))\n"
"  end\n"
"\n"
"  while parse_event(reader, events) do\n"
"    -- Empty body.\n"
"  end\n"
"\n"
"  return events\n"
"end\n"
"\n"
"return M\n"
""
;
