Skip to content

Commit

Permalink
[trace-view] Updated README file and finessed error checks (#20320)
Browse files Browse the repository at this point in the history
## Description 

This PR updates the extension's README file and finesses some checks and
error messages issues when traces cannot be found or are empty.

## Test plan 

Tested manually
  • Loading branch information
awelc authored Nov 20, 2024
1 parent 2cfe9b6 commit cecb0e6
Show file tree
Hide file tree
Showing 4 changed files with 83 additions and 45 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -330,6 +330,9 @@ export function readTrace(
filesMap: Map<string, IFileInfo>
): ITrace {
const traceJSON: JSONTraceRootObject = JSON.parse(fs.readFileSync(traceFilePath, 'utf8'));
if (traceJSON.events.length === 0) {
throw new Error('Trace contains no events');
}
const events: TraceEvent[] = [];
// We compute the end of lifetime for a local variable as follows.
// When a given local variable is read or written in an effect, we set the end of its lifetime
Expand Down
54 changes: 40 additions & 14 deletions external-crates/move/crates/move-analyzer/trace-debug/README.md
Original file line number Diff line number Diff line change
@@ -1,20 +1,46 @@
# Move Trace Debug
# Move Trace Debugging

Provides the ability to visualize Move trace files, which can be generated for a given package when running Move tests. These trace files contain information about which Move instructions are executed during a given test run. This extenstion leverages an implementation of the [Debug Adapter Protocol](https://microsoft.github.io/debug-adapter-protocol) (DAP) that analyzes Move execution traces and presents them to the IDE client (in this case a VSCode extension) in a format that the client understands and can visualize using a familiar debugging interface.
Provides the ability to visualize Move trace files, which can be generated for a given package when running Move tests. These trace files contain information about which Move instructions are executed during a given test run. This extension leverages an implementation of the [Debug Adapter Protocol](https://microsoft.github.io/debug-adapter-protocol) (DAP) that analyzes Move execution traces and presents them to the IDE client (in this case a VSCode extension) in a format that the client understands and can visualize using a familiar debugging interface.

## Supported features

Currently we support trace-debugging of Move unit tests only. and the following trace-debugging features are supported:
- stepping forward through the trace (step, next, step out, and continue commands)
- tracking local variable values (including enums/structs and references)
- line breakpoints

Note that support for trace-debugging macros and enums is limited at this point - stepping through macros or code related to enums may result in somewhat unexpected results due to how these constructs are handled internally by the Move execution framework. In particular, variable value tracking may be affected when trace-debugging these constructs. Work is ongoing to improve state-of-the-art - improvement suggestions and bug reports files as issues agains Sui's GitHub [repository](https://github.com/MystenLabs/sui) are greatly appreciated.

# How to Install

When this extension and its companion DAP implementation become more mature, we will make it available in the VSCode Marketplace. At the moment, in order to experiment with it must be built and installed locally (start in the main directory of this extension):
1. Install dependencies for the extension:
```bash
npm install
```
2. Install dependencies for the DAP implementation:
```bash
npm install --prefix ../move-trace-adapter
```
3. Package the extension (it will pull relevant files from the DAP implementation)
1. Open a new window in any Visual Studio Code application version 1.61.0 or greater.
2. Open the command palette (`` + `` + `P` on macOS, `^` + `` + `P` on Windows and GNU/Linux,
or use the menu item *View > Command Palette...*) and
type **Extensions: Install Extensions**. This will open a panel named *Extensions* in the
sidebar of your Visual Studio Code window.
3. In the search bar labeled *Search Extensions in Marketplace*, type **Move Trace Debugger**. The Move Trace debugger extension
should appear as one of the option in the list below the search bar. Click **Install**.

# How to trace-debug a Move unit test

Debugging a Move unit tests consists of two steps: generating a Move trace and actually trace-debugging it.

## Generating a Move trace

If you have [Mysten's Move extension](https://marketplace.visualstudio.com/items?itemName=mysten.move) installed you can generate a Move trace for tests defined in a given file by navigating to this file in VSCode and running `Move: Trace Move test execution` from the command palette. See the description of [Mysten's Move extension](https://marketplace.visualstudio.com/items?itemName=mysten.move) for pre-requisites needed to run this command.

If you plan to use the the Trace Debugging Extension by itself, you need to generate the traces using command-line interface of `sui` binary. See [here](https://docs.sui.io/guides/developer/getting-started/sui-install) for instructions on how to install `sui` binary. Note that the `sui` binary must be built with the `tracing` feature flag. If your version of the `sui` binary was not built with this feature flag, an attempt to trace test execution will fail. In this case you may have to build the `sui` binary from source following these [instructions](https://docs.sui.io/guides/developer/getting-started/sui-install#install-sui-binaries-from-source).

Once the `sui` binary is installed, you generate traces for all test files in a given package by running the following command in the package's root directory:
```shell
sui move test --trace-execution
```
vsce package -o move-trace-debug.vsix

You can limit trace generation to the tests whose name contains a filter string by passing this string as an additional argument to the trace generation command:
```shell
sui move test FILTER_STRING --trace-execution
```
4. Install move-trace-debug.vsix in VSCode using `Install from VSIX...` option

## Trace-debugging a test

Once traces are generated, open a Move file containing the test you want to trace-debug and execute `Run->Start Debug` command. The first time you execute this command, you will have to choose the debugging configuration for Move files, of which there should be only one available. Then you will have to choose a test to trace-debug if there is more than one test in a file (otherwise a trace-debugging session for a single test will start automatically).
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "move-trace-debug",
"displayName": "move-trace-debug",
"displayName": "Move Trace Debugger",
"description": "An extension to visualize Move VM traces DAP-style",
"version": "0.0.1",
"preview": true,
Expand Down Expand Up @@ -56,7 +56,7 @@
},
"logLevel": {
"type": "string",
"description": "Logging lavel for the Debug Adapter Protocol.",
"description": "Logging level for the Debug Adapter Protocol.",
"enum": [
"none",
"log",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -186,26 +186,26 @@ class MoveConfigurationProvider implements vscode.DebugConfigurationProvider {
async function findTraceInfo(editor: vscode.TextEditor): Promise<string> {
const pkgRoot = await findPkgRoot(editor.document.uri.fsPath);
if (!pkgRoot) {
throw new Error("Cannot find package root for file: " + editor.document.uri.fsPath);
throw new Error(`Cannot find package root for file '${editor.document.uri.fsPath}'`);
}

const pkgModules = findModules(editor.document.getText());
if (pkgModules.length === 0) {
throw new Error("Cannot find any modules in file: " + editor.document.uri.fsPath);
throw new Error(`Cannot find any modules in file '${editor.document.uri.fsPath}'`);
}

const tracedFunctions = findTracedFunctions(pkgRoot, pkgModules);

if (tracedFunctions.length === 0) {
throw new Error("No traced functions found for package at: " + pkgRoot);
throw new Error(`No traced functions found for package at '${pkgRoot}'`);
}

const fun = tracedFunctions.length === 1
? tracedFunctions[0]
: await pickFunctionToDebug(tracedFunctions);

if (!fun) {
throw new Error("No function to be debugged selected from\n" + tracedFunctions.join('\n'));
throw new Error(`No function to be trace-debugged selected from\n` + tracedFunctions.join('\n'));
}

return fun;
Expand Down Expand Up @@ -261,35 +261,44 @@ function findModules(file_content: string): string[] {
/**
* Find all functions that have a corresponding trace file.
*
* @param pkg_root root directory of the package.
* @param pkg_modules modules in the package of the form `<package>::<module>`.
* @param pkgRoot root directory of the package.
* @param pkgModules modules in the package of the form `<package>::<module>`.
* @returns list of functions of the form `<package>::<module>::<function>`.
* @throws Error (containing a descriptive message) if no trace files are found for the package.
*/
function findTracedFunctions(pkg_root: string, pkg_modules: string[]): string[] {
try {
const traces_dir = path.join(pkg_root, 'traces');
const files = fs.readdirSync(traces_dir);
const result: [string, string[]][] = [];

pkg_modules.forEach((module) => {
const prefix = module.replace(/:/g, '_') + '__';
const prefixFiles = files.filter((file) => file.startsWith(prefix));
const suffixes = prefixFiles.map((file) => {
const suffix = file.substring(module.length);
if (suffix.startsWith('__') && suffix.endsWith('.json')) {
return suffix.substring(2, suffix.length - 5);
}
return suffix;
});
result.push([module, suffixes]);
});
function findTracedFunctions(pkgRoot: string, pkgModules: string[]): string[] {

function getFiles(tracesDir: string): string[] {
try {
return fs.readdirSync(tracesDir);
} catch (err) {
throw new Error(`Error accessing 'trace' directory for package at '${pkgRoot}'`);
}
}
const tracesDir = path.join(pkgRoot, 'traces');

return result.map(([module, functionName]) => {
return functionName.map((func) => module + "::" + func);
}).flat();
} catch (err) {
return [];
const filePaths = getFiles(tracesDir);
if (filePaths.length === 0) {
throw new Error(`No trace files for package at ${pkgRoot}`);
}
const result: [string, string[]][] = [];

pkgModules.forEach((module) => {
const prefix = module.replace(/:/g, '_') + '__';
const prefixFiles = filePaths.filter((filePath) => filePath.startsWith(prefix));
const suffixes = prefixFiles.map((file) => {
const suffix = file.substring(module.length);
if (suffix.startsWith('__') && suffix.endsWith('.json')) {
return suffix.substring(2, suffix.length - 5);
}
return suffix;
});
result.push([module, suffixes]);
});

return result.map(([module, functionName]) => {
return functionName.map((func) => module + "::" + func);
}).flat();
}

/**
Expand Down

0 comments on commit cecb0e6

Please sign in to comment.