Skip to content

feat: support debug FunC compilation with source maps #2923

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 3 commits into
base: main
Choose a base branch
from

Conversation

i582
Copy link
Member

@i582 i582 commented Apr 23, 2025

Debug information in the form of source maps is crucial for tools such as coverage analyzers and debuggers. This PR is a step toward creating those tools, but first we need a way to extract source maps from FunC.

A new WASM build based on this PR is available here: tact-lang/ton#3

In debug mode, the FunC compiler inserts pseudo-instructions called DEBUGMARK, each tagged with a unique ID corresponding to its source statement. After compilation, the FunC compiler returns a mapping from each ID to its source location, allowing us to correlate specific TVM instructions with FunC code.

Because DEBUGMARK isn’t a real TVM instruction, we can’t run the compiled BoC directly. In subsequent PRs, we’ll introduce TASM and an assembler for it. This assembler will leverage the DEBUGMARK instructions to build the instruction-to-statement mapping and then strip them out, producing a valid BoC ready for execution

Pseudo-instruction??

Why do we need a pseudo-instruction? Why not just reuse the original PR’s DEBUGSTR approach?

Embedding additional real instructions in the executable code creates several problems:

Gas overhead
Every extra instruction consumes gas, so debug-mode binaries will behave (and cost) differently than usual builds.
Code-size bloat
Inserting additional instructions can push the BoC into a new TVM cell, which then costs an extra 110 gas just to unpack.
Tooling complexity
Debuggers and coverage tools must include special logic to recognize and skip these “debug” instructions at runtime.

Previous attempts

We’ve already explored collecting instruction mappings directly in Tact code. The old approach worked like this:

  • ID assignment. During FunC code generation, each source statement was assigned a unique ID.
  • Code tagging. We injected a PUSHINT X DROP sequence into the bytecode for every statement, safely marking its location.
  • Runtime extraction. When the contract ran, the sandbox logs captured those PUSHINT X calls, and a post-processing step reconstructed the mapping.

The key drawback was that it relied on executing instructions at runtime. In contrast, the new solution performs all mapping work at compile time, without adding any code to the BoC

@i582 i582 requested a review from a team as a code owner April 23, 2025 15:44
@novusnota novusnota added this to the Infra: 2025-05 milestone Apr 23, 2025
};

export type FuncConfig = {
sources: string[];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

readonly

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants