{"id":13992288,"url":"https://github.com/cloudflare/loom","last_synced_at":"2025-03-21T21:07:55.926Z","repository":{"id":66145855,"uuid":"79233909","full_name":"cloudflare/loom","owner":"cloudflare","description":"Easier to read LuaJIT dumps","archived":false,"fork":false,"pushed_at":"2024-09-25T05:04:25.000Z","size":388,"stargazers_count":179,"open_issues_count":7,"forks_count":17,"subscribers_count":14,"default_branch":"trunk","last_synced_at":"2025-03-13T23:01:47.717Z","etag":null,"topics":["bytecode","debugging","lua","luajit","trace-report"],"latest_commit_sha":null,"homepage":"","language":"Lua","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/cloudflare.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2017-01-17T14:10:52.000Z","updated_at":"2025-03-12T14:57:24.000Z","dependencies_parsed_at":"2025-03-01T23:35:45.852Z","dependency_job_id":null,"html_url":"https://github.com/cloudflare/loom","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cloudflare%2Floom","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cloudflare%2Floom/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cloudflare%2Floom/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cloudflare%2Floom/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/cloudflare","download_url":"https://codeload.github.com/cloudflare/loom/tar.gz/refs/heads/trunk","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":244706537,"owners_count":20496571,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["bytecode","debugging","lua","luajit","trace-report"],"created_at":"2024-08-09T14:01:55.050Z","updated_at":"2025-03-21T21:07:55.887Z","avatar_url":"https://github.com/cloudflare.png","language":"Lua","readme":"LOOM\n====\n\nIt's a replacement / enhancement of the `-jdump` option included in LuaJIT.\n\nAs a command line argument\n===\n\nJust put it in a `jit/` directory within `package.path` or `$LUA_PATH`, typically `'/usr/local/share/luajit-2.1..../jit/'`; but it also works in `'/usr/local/share/lua/5.1/jit/'` or even `'./jit/'`.  Then it can be used as an argument to LuaJIT in the form:\n\n**`-jloom[=\u003ctmpl\u003e[,\u003cout\u003e]]`**\n\n`\u003ctmpl\u003e` is a template file (default `'loom.html'`) and `\u003cout\u003e` is an output file name (default `io.stdout`).\n\nLua API\n===\n\nIf you want to report traces on just part of your code, it's better to use it explicitly.\n\n**`local loom = require 'jit.loom'`**\n\nAs any module, you have to `require()` it first.\n\n**`loom.on()`**\n\nStarts recording all JIT events and traces.\n\n**`traces, funcs = loom.off()`**\n\n**`report = loom.off([f [, ...]])`**\n\nStops recording and performs any processing and cross references needed to actually generate a report.\n\nCalled without any arguments, returns two Lua tables, one with the processed trace information and a second one with all the functions involved in those traces execution.\n\nThe second form is equivalent to\n\n    do\n        local traces, funcs = loom.off()\n        report = f(traces, funcs, ...)\n    end\n\nThat is, both return values (the `traces` and `funcs` arrays) are passed to the given function `f`, together with any extra argument, and returns any return value(s) of `f`.\n\n**`loom.start(tmpl, out)`**\n\nImplements the `-jloom[=tmpl[,out]]` option. The `tmpl` argument is passed to `loom.template()` to create a reporting function.  If omitted, defaults to `'loom.html'`.  The `out` parameter is either a writeable open file or a file name where the report is written into (after formatting by the template), defaults to `io.stdout`.  When the Lua VM is terminated normally, `loom.off()` is called with the reporting function created by the given template.,\n\n### Utility Functions\n\nThere are some functions included in the `loom` package to help formatting a report.\n\n**`f = loom.template(tmpl)`**\n\nThe string `tmpl` is a report template using the template syntax described below.  If it doesn't contain any line break, is interpreted as a pathname to read the template from a text file.\n\nThe template is compiled into a Lua function that takes some arguments (named with `{@ name ...}` tags) and outputs the result as a string.\n\n\n**`loom.annotated(funcs, traces)`**\n\nReturns an annotated listing of the source code of the given `funcs` and `traces` arrays.\n\n**`loom.allipairs(t)`**\n\nLike `ipairs(t)`, but stops at `table.maxn(t)` instead of the first `nil` value.\n\n**`loom.sortedpairs(t)`**\n\nReturns an iterator that visits the same pairs as `pairs(t)`, but sorted by keys.\n\n\nTemplate syntax\n===\n\nThe included template implementation is based on Danila Poyarkov's [lua-template](https://github.com/dannote/lua-template), with a syntax more like Django's or Handlebar's, to make it more friendly to editors that help with HTML content.\n\n\n**`{% lua code %}`**\n\nEmbeds any Lua code\n\n**`{{ expression }}`**\n\nOutputs the result of the Lua expression, with the `\u0026`, `\"`, `\u003c` and `\u003e` characters escaped.\n\n**`{{= expression }}`**\n\nOutputs the result of the Lua expression verbatim, without any character escaping.\n\n**`{{: 'fmt', args, ... }}`**\n\nOutputs the result of `string.format(fmt, args, ...)` without any escaping.\n\n**`{@ name ... }`**\n\nDefines template argument names.  Each `name` must be a valid Lua variable name (that is, a sequence of letters, numbers or underscores not beginning with a number), separated by commas or spaces (or any non-alfanumeric-underscore character).\n\n\nIncluded Template\n===\n\nThe included `loom.html` template renders the trace report as an HTML document.  It's divided in two sections: a Sourcecode -\u003e Bytecode -\u003e Traces one, and a list of traces, with the Bytecode -\u003e IR -\u003e mcode progression for each one.\n\n1.- Source list\n---\n\nFor each function that appears in the traces, the source code is shown on the left column (if the source was in a file) and the bytecode at right of it with a random background color (a different one for each function).\n\nThe bytecode is shown in the order it appears in memory, so some source lines are in a different order as in the original source or repeated, for example the opening of a `for` loop appears again (in a lighter gray) in the bottom of the loop.\n\nAt the right of the bytecode, there are links of the form `\u003ctr\u003e/\u003cseq\u003e`, showing some trace executed it at some sequential number.  For example `3/4` means it's the fourth bytecode executed by trace #3.  A single bytecode can appear several times in the same trace (for example, when unrolling a loop).  Each trace have assigned a random background color.\n\nTrace start and abort events are also marked to the right of the relevant bytecode, with `[n=xx]` notes telling how many times they happened until the trace gets compiled or blacklisted.\n\n2.- Trace list\n---\n\nEach trace gets a three-column table framed on the same color as the respective links in the previous section.\n\nThe first column lists the bytecode in the order it was executed.  On the left there are links that go back to the same bytecode on the previous section.  The bytecodes keep the same background colour of the function.  Clicking on the column title toggles source code annotations.\n\nThe second column lists the IR code generated by the trace.  While the total semantics is supposed to be maintained, there's no direct correspondence between IR and bytecode instructions.  For example, as much code as possible is moved before the start of an inner loop, and compilable library functions are just checked (to assure they're the right functions) and IR code is emitted instead of a call to the function.\n\nSnapshot points are inserted in the IR code, but the snapshot content isn't shown by default.  To see them, either hover the mouse over the snapshot or click in the column title to reveal all at the same time.\n\nThe third column is the generated mcode that is natively executed by the processor.  Exit points are labelled by the snapshot number or a trace number if a later trace patched itself in.\n\nExamples\n===\n\nThe 'sample.lua' file includes some small code snippets to play with.  For example, the comments about `-jv` option show:\n\n    luajit -jv -e \"for i=1,1000 do for j=1,1000 do end end\"\n\nTo output just two lines (one per trace).  Changing to `-jdump` results in:\n\n    ---- TRACE 1 start (command line):1\n    0009  FORL     4 =\u003e 0009\n    ---- TRACE 1 IR\n    0001    int SLOAD  #5    CI\n    0002  + int ADD    0001  +1\n    0003 \u003e  int LE     0002  +1000\n    0004 ------ LOOP ------------\n    0005  + int ADD    0002  +1\n    0006 \u003e  int LE     0005  +1000\n    0007    int PHI    0002  0005\n    ---- TRACE 1 mcode 47\n    0bcbffd1  mov dword [0x41a94410], 0x1\n    0bcbffdc  cvttsd2si ebp, [rdx+0x20]\n    0bcbffe1  add ebp, +0x01\n    0bcbffe4  cmp ebp, 0x3e8\n    0bcbffea  jg 0x0bcb0014\t-\u003e1\n    -\u003eLOOP:\n    0bcbfff0  add ebp, +0x01\n    0bcbfff3  cmp ebp, 0x3e8\n    0bcbfff9  jle 0x0bcbfff0\t-\u003eLOOP\n    0bcbfffb  jmp 0x0bcb001c\t-\u003e3\n    ---- TRACE 1 stop -\u003e loop\n\n    ---- TRACE 2 start 1/3 (command line):1\n    0010  FORL     0 =\u003e 0005\n    0005  KSHORT   4   1\n    0006  KSHORT   5 1000\n    0007  KSHORT   6   1\n    0008  JFORI    4 =\u003e 0010\n    ---- TRACE 2 IR\n    0001    num SLOAD  #1    I\n    0002    num ADD    0001  +1\n    0003 \u003e  num LE     0002  +1000\n    ---- TRACE 2 mcode 81\n    0bcbff79  mov dword [0x41a94410], 0x2\n    0bcbff84  movsd xmm6, [0x403382b8]\n    0bcbff8d  movsd xmm5, [0x403382c8]\n    0bcbff96  movsd xmm7, [rdx]\n    0bcbff9a  addsd xmm7, xmm6\n    0bcbff9e  ucomisd xmm5, xmm7\n    0bcbffa2  jb 0x0bcb0014\t-\u003e1\n    0bcbffa8  movsd [rdx+0x38], xmm6\n    0bcbffad  movsd [rdx+0x30], xmm6\n    0bcbffb2  movsd [rdx+0x28], xmm5\n    0bcbffb7  movsd [rdx+0x20], xmm6\n    0bcbffbc  movsd [rdx+0x18], xmm7\n    0bcbffc1  movsd [rdx], xmm7\n    0bcbffc5  jmp 0x0bcbffd1\n    ---- TRACE 2 stop -\u003e 1\n\nTo recreate under loom, try:\n\n    luajit -jloom -e \"require('sample').lulu()\" \u003e out.html\n\nAnd open the resulting `out.html` with a browser to see the same thing with nice colours and links to help following how the traces flow together.\n\n![screenshot](shot.png)\n\n","funding_links":[],"categories":["Lua"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcloudflare%2Floom","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcloudflare%2Floom","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcloudflare%2Floom/lists"}