Skip to content

Implement basic CodeQL debug adapter#2291

Merged
dbartol merged 39 commits intomainfrom
dbartol/debug-adapter
Apr 18, 2023
Merged

Implement basic CodeQL debug adapter#2291
dbartol merged 39 commits intomainfrom
dbartol/debug-adapter

Conversation

@dbartol
Copy link
Copy Markdown
Contributor

@dbartol dbartol commented Apr 10, 2023

This PR is the initial implementation of a Debug Adapter Protocol-based debug engine for CodeQL. Implementing the DAP allows us to take advantage of all of VS Code's existing UI around debugging, including configuration via launch.json, the "Start Debugging" and "Start Without Debugging" commands, and the Debug Console. Later, we'll build further UI on top of this work, and perhaps add some advanced debugging features.

For now, our DAP implementation runs in the extension process, and invokes the query server via the same code that we were already using for "Run Query" and other local query commands. The existing commands do not go through the debug adapter yet. Instead, I've added a few new commands under the canary flag, and I've enabled the regular VS Code "Start Debugging" (F5) and "Start Without Debugging" (Ctrl-F5) commands to work for CodeQL.

Configuration

In "launch.json", there is a new available debug configuration type, named "codeql". Its configuration options are as follows:

  • query - The path to the query to debug. This query will be treated as the root query of the compilation, even when doing quick eval. Thus, the quick eval selection can now be in a different file from the actual query, allowing debugging of queries that extend abstract class, instantiate parameterized modules, etc. If query is undefined, the currently active ".ql" file will be used, just like the "Run Query" command.
  • database - The path to the target database directory. If database is undefined, the currently selected database from the database panel will be used, just like the "Run Query" command.
  • extensionPacks - An array of the pack names (not paths) of the extension packs to use. Each of these packs must be findable along the additionalPacks path. An empty array specifies that no extension packs are to be used. If extensionPacks is undefined, the default depends on the codeQL.useExtensionPacks setting: If true, then all extension packs in the current workspace will be used. If false, no extension packs will be used.
  • additionalPacks - An array of paths to search for additional library packs. This is equivalent to the --additional-packs CLI option. If additionalPacks is undefined, the default is to use the list of root folders in the current workspace, as we already do for the other commands.
  • quickEval - A boolean value to specify whether to perform a quickeval of the current selection instead of a regular query evaluation. Users will rarely set this explicitly in "launch.json". It exists primarily so that the new "CodeQL: Debug Selection" command can force it to true.

Commands

The "CodeQL: Run Query" and "CodeQL: Quick Evaluation" commands work exactly as before. They ignore any debug configuration, and do not start a debug session. Later, once we're more confident in the debugger UX, we'll make all of those commands go through the debug configuration.

If a "codeql" debug configuration is selected on the Debug panel, hitting F5 ("Start Debugging") will launch a debug session with the active debug configuration. This will bring up the VS Code debug toolbar, and will evaluate the configured query against the configured database. Results are displayed in the results view as before, and the query history panel is updated. The most visible change is that all of the query server log output will now go to the Debug Console. The Debug Console is specific to that debug session, so you don't have to search through the query server output pane to make sure you're looking at the right query run.

The debug session will stay active after evaluation is complete. It can be terminated by clicking the stop (red square) button on the debug toolbar, or hitting Shift-F5.

Hitting Ctrl-F5 ("Start Without Debuggin") with a "codeql" debug configuration active will evaluate the query, display the results, and immediately terminate the debug session. This is most similar to the behavior of the existing "Run Query" command.

While a "codeql" debug session is active, the "CodeQL: Debug Selection" command will perform a quickeval of the current selection, in the context of the active debug session. If no debug session is active, but a "codeql" debug configuration is selected in the Debug panel, then this command launches a new debug session with that debug configuration to perform the quickeval.

While a "codeql" debug session is active, the "Continue" (F5) command will re-evaluate the entire query.

The "Step Into", "Steo Out", and "Step Over" commands don't do anything (yet).

Code Changes

package.json has been updated to provide the "codeql" debug configuration type. Note that this is available even without the canary flag, but any attempt to start a CodeQL debug session will display an error stating that the feature is not yet available. It also adds the new commands.

debug-configuration.ts resolves the debug configuration from "launch.json" to the actual launch parameters needed by the debug engine. VS Code calls the resolveDebugConfiguration() function to fill in defaults before any ${...} variables are resolved, and then calls resolveDebugConfigurationWithSubstitutedVariables() to give us a chance to finalize the launch configuration after variable substitution. It is in the latter function that we determine the quickeval context if needed, and validate that the user has specified a query and a database.

debug-protocol.ts defines a few custom messages we've added beyond the standard DAP messages. We've added one new request (to run a quickeval with a specific selection), and two events (one at the start of evaluation that reports the output directory and quickeval context, and one at the end of evaluation that reports the evaluation time and any error message).

debug-session.ts is the actual implementation of the DAP, using a helper library provided by VS Code. This is actually pretty simple, since it just invokes the query server to do the heavy lifting.

debugger-factory.ts implements the factory that creates DebugSession objects when VS Code starts a new debug session.

debugger-ui.ts implements the new debugger commands, and handles the display of results after each successful evaluation.

local-databases-ui.ts doesn't really do anything new, but I did have to update how it handles progress when prompting the user to select a database. Before, it assumed that it always had a progress callback provided by the invoking command. However, when invoked during debug launch to fill in a missing database property on the configuration, it needs to create its own progress bar if it has to load a database.

local-databases.ts now separates the creation of a DatabaseItem from the registration of a DatabaseItem in the list of available databases. This allows a debug configuration to specify a path to a database that might not be present in the database manager for the current workspace.

local-queries.ts and run-queries-shared.ts got a bit of refactoring around how we compute query configuration. I've split up determineSelectedQuery() so that each configuration property (query, quickeval, etc.) can be computed independently. I also removed the assumption that the quickeval selection is in the same document as the actual query being compiled.

Tests

The new debugger tests I've added exercise the new commands, and validate that we evaluation the correct files and selections, and get the correct number of results. This is all done via a DebugController class that turns the event-driven style of the debug adapter into an async/await based sequential style. The test issues debugger commands, then asserts that they caused the expected events.

I've also added new flavors to the existing query tests to perform the same evaluation while going through the debugger instead of localQueries. This also uses the DebugController when using the debugger.

Loading
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Complexity: High Requires careful design or review. enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants