nupp.profile.trace

Stable identities shared by static trace checking and the opt-in runtime collector.

The recorder's strings are presentation, not API. A trace event is first interpreted under the exact VM profile that emitted it, then mapped to one of the identities here. Static checks use those same records, which is what keeps a bytecode finding, an @jit contract error and an observed abort from acquiring three explanations.

local trace = nupp.profile.trace

local reason = trace.forOpcode("FNEW")
print(reason.id, reason.repair)

See LuaJIT trace checking for the static side of this, and nupp.profile for the collector that reports the runtime side.

Module contents

Types

TypeKindDescription
ProfiletypeThe recorder a trace event has to be interpreted under.
ReasontypeOne stable reason the JIT declined to record, however it was observed.

Functions

FunctionKindDescription
forOpcodefunctionAnswers the record for a bytecode LuaJIT has no recorder for.
forSourcefunctionAnswers the record a static finding maps to.
opcodeNamefunctionRenders a bytecode number as the name LuaJIT knows it by.
profilefunctionDescribes the recorder this process is running.
reasonfunctionAnswers the record filed under one stable identity.
recordsfunctionAnswers every record, in registry order.
runtimefunctionInterprets one raw abort the VM reported.

Types#

Profiletype#

type trace.Profile = {
    id: string,
    luajitRevision: string,
    luajitVersion: integer,
    architecture: string,
    operatingSystem: string,
    enabledRecorderFeatures: {string},
    bytecodeSchema: string,
    supported: boolean
}

The recorder a trace event has to be interpreted under.

Reasons are mapped from VM text and bytecode numbering, and both move between builds, so an event is only comparable to another taken under the same id.

Reasontype#

type trace.Reason = {
    id: string,
    class: 'blocker' | 'risk' | 'stop',
    opcodes: {[string]: boolean}?,
    source: {[string]: boolean}?,
    runtime: {string}?,
    explanation: string?,
    repair: string?,
    lint: string?,
    contractSeverity: string?
}

One stable reason the JIT declined to record, however it was observed.

class is what the reason is worth acting on: a blocker cannot record at all, a risk may or may not sit on a hot path, and a stop is trace formation working as designed. The three index fields are the routes to it, from a bytecode name, from a static finding, and from the VM's own runtime text.

Functions#

trace.forOpcodefunction#

function trace.forOpcode(opcode: string): trace.Reason?

Answers the record for a bytecode LuaJIT has no recorder for.

Arguments

NameTypeDescription
opcodestring

the bytecode's name, as opcodeName renders it

Returns

TypeDescription
trace.Reason?

the record, or nil when that bytecode records

trace.forSourcefunction#

function trace.forSource(source: string): trace.Reason?

Answers the record a static finding maps to.

This is what keeps a checker finding and an observed abort from explaining the same thing two different ways.

Arguments

NameTypeDescription
sourcestring

the static finding's name

Returns

TypeDescription
trace.Reason?

the record, or nil when the finding has no runtime counterpart

trace.opcodeNamefunction#

function trace.opcodeName(opcode: integer): string

Renders a bytecode number as the name LuaJIT knows it by.

Arguments

NameTypeDescription
opcodeinteger

the number the VM reported

Returns

TypeDescription
string

the trimmed name

trace.profilefunction#

function trace.profile(): trace.Profile

Describes the recorder this process is running.

Returns

TypeDescription
trace.Profile

the profile every event from this process is interpreted under

trace.reasonfunction#

function trace.reason(id: string): trace.Reason?

Answers the record filed under one stable identity.

Arguments

NameTypeDescription
idstring

the identity, as a report or a diagnostic carries it

Returns

TypeDescription
trace.Reason?

the record, or nil when nothing is filed under it

trace.recordsfunction#

function trace.records(): {trace.Reason}

Answers every record, in registry order.

The list is a copy, so a caller sorting or filtering it cannot disturb the registry the checker reads.

Returns

TypeDescription
{trace.Reason}

one entry per known reason

trace.runtimefunction#

function trace.runtime(errorCode: integer?, errorArg: number | string | nil): trace.Reason, string

Interprets one raw abort the VM reported.

An abort naming an unrecordable bytecode is routed by that opcode rather than by its text, so it lands on the same record a static check would have found. Anything unrecognized answers jit/runtime-unknown rather than nothing.

Arguments

NameTypeDescription
errorCodeinteger?

the VM's trace error number, or nil

errorArgnumber | string | nil

whatever the VM passed beside it

Returns

TypeDescription
trace.Reason

the record it maps to

string

the rendered text, for display