NEP: 19 Title: Debug Info Specification Author: Harry Pierson ([email protected]) Type: Standard Status: Final Created: 2019-09-02
This NEP describes the debug information format used by the Neo Smart Contract Debugger. This information is generated by smart contract compilers such as NCCS and neo-boa.
In order to provide a good developer experience, the debugger needs additional type information about parameters, variables and storage items that exists in the contract source code but is not needed by the NeoVM and is discarded during contract compilation. The debugger also needs to source map information in order to map binary addresses in compiled contracts to locations in source code.
This format has been implemented by the Neo Smart Contract Debugger and multiple Neo smart contract compilers including NCCS, NEON, neo-boa, Neow3j and neo-go.
Neo compilers SHOULD emit debug information as part of the compilation process along with the required compiled contract binary and contract manifest. Emitting debug information is optional, but the Neo Smart Contract Debugger will be limited to disassembly level debugging without it.
Debug info is stored in JSON format, described informally below and specified via the "neo-debug-info.schema.json" file. The debug info is stored in a file with the same base name as the contract binary with the extension ".debug.json". The debug info can be optionally compressed using standard Zip compression. When compressed, the debug info archive must have single ".debug.json" file and the archive itself must have an ".nefdbgnfo" extension.
Note, the format is defined in this specification using TypeScript for readability. There is no requirement that this format be implemented in TypeScript. The full specification of this format is specified in "neo-debug-info.schema.json"
The debug info has the following structure. Note, for space optimization, several string properties contain multiple pieces of information encoded as a string. Those encodings are indicated in comments in the code below.
type TypeName = string; // format: ContractParamterType enum value
type MemberName = string // format: "{namespace},{display-name}
type Variable = string; // format: "{name},{TypeName}(,{slotIndex})?
interface Method { id: string; name: MemberName; range: string; // format: "{start-address}-{end-address} params?: Variable[]; return?: TypeName; variables?: Variable[]; "sequence-points"?: string[]; // format: "{address}[{document-index}]{start-line}:{start-column}-{end-line}:{end-column}" }
interface Event { id: string; name: MemberName; params?: Variable[]; }
interface DebugInformation { hash: string; // hex-encoded UInt160 documents?: string[]; // absolute or relative file paths document-root?: string | null; // project root events?: Event[]; methods?: Method[]; "static-variables"?: Variable[]; }
TypeNames in Neo debug info are string encoded values from the ContractParameterType enum type
- Any
- Boolean
- Integer
- ByteArray
- String
- Hash160
- Hash256
- PublicKey
- Signature
- Array
- Map
- InteropInterface
- Void
Variable types are used to encode name and type information about NeoVM arguments, local variables and static fields. Additionally, a variable may include an optional slot index. This is useful for scenarios where the compiler may use slots for hidden variables not authored by the developer. If the slot index is not specified, the array index of the Variable type is used as the slot index. For a given variable array, ALL variables MUST include a slot index if ANY variables contain a slot index. Mixing variables with and and without an optional slot index in a single Variable array is NOT SUPPORTED. It is supported to have some Variable arrays include slot index information while other Variable arrays in the same debug info file do not.
Name, type and optional slot index are combined into a single comma separated string. A Variable without slot index has a single comma (i.e. "varName,varType") while a Variable with index has two commas (i.e. "varName,varType,1").
MemberName types are used to store the name and optional namespace of a type member such as a method or event. MemberName namespace is optional, but the comma separator is not. For encoding members with no namespace, the MemberName string MUST start with a comma (i.e. ",SomeName").
Method types have the following fields:
- id: a unique string representing the method.
- name: a MemberName with the method's name and optional namespace
- range: the range of NeoVM bytecode addresses that is associated with this method. Range is encoded as a string with the start and end addresses as integers separated by a dash
- params: a collection of Variable instances representing the NeoVM arguments associated with this method
- return: the TypeName of the method's return value
- variables: a collection of Variable instances representing the NeoVM local variables associated with this method
- sequence-points: a collection of strings that encode a map of NeoVM bytecode addresses back to source code locations.
A sequence point contains six integers encoded into a single string
- address: This is an integer representing the location in the contract script of the sequence point.
- document-index: This is an index into the documents array indicating the source code file containing the sequence point. documents array is described below.
- start-line: This is the line in the source code file that is associated with the sequence point
- start-column: This is the column of the line specified above in the source code file associated with the sequence point. Note, this value can be zero for languages that don't support mapping sequence points to segments within a line
- end-line: for languages that support multi-line code expressions, this is the last line of the source code associated with the sequence point. For single-line expressions, this will be the same as start-line
- end-column: for languages that support multi-line code expressions, this is column within end-line that marks the end of the sequence point code expression. Like start-column, it can be zero. Sequence points that have the same start/end line and zero for both start/end column will render the sequence point as the full line specified.
{address}[{document-index}]{start-line}:{start-column}-{end-line}:{end-column}
Event types have the following fields:
- id: a unique string representing the method.
- name: a MemberName with the method's name and optional namespace
- params: a collection of Variable instances representing the NeoVM arguments associated with this event
Top level debug information has the following fields
This property stores the UInt160 hash value of the contract's Script. Note, this is NOT the same as a deployed contract's script hash. The debugger uses this hash value to map deployed contracts to their debug information. The hash value is stored as a hex encoded string with an optional "0x" prefix.
This property stores an array of file paths, used in sequence point data. These paths can be absolute or relative, pointing to the file paths of source files as they existed on the machine where the contract was compiled.
Neo Smart Contract Debugger has the ability to automatically discover differences in paths between compiling and debugging machine, plus supports manual source file mapping for cases where the mapping cannot be determined automatically.
If omitted, this property defaults to an empty array.
This property stores the root folder path for the contract source files. Releative paths in the documents array are treated as relative to the document-root path.
If omitted or null, all elements of the documents array are treated as absolute paths.
This property stores an array of Variable types, representing the static fields associated with this contract. If omitted, this property defaults to an empty array.
This property stores an array of Method types as described above. Each Method object represents a method in the contract. Both private and public methods from the contract should be represented in the methods array. If omitted, this property defaults to an empty array.
This property stores an array of Event types as described above. Each Event object represents a parameters of a contract notification that may be fired during contract execution. If omitted, this property defaults to an empty array.
Initial preview releases of the Neo Smart Contract Debugger for Neo Legacy used a more verbose format for debug info. That format was incompatible with the format described in this document, but never shipped a production release. Production releases of the Neo Legacy debugger use a slightly different version of the format described in this document.
The Neo N3 Debugger initially shipped during preview without the DebugInformation static-variables property or the slotIndex Variable value. These were added as optional fields before production release of the N3 Debugger.
Details regarding older versions of the debug info format is available in the original Design Note.