Skip to content

Implement cProfile-based profiling for performance analysis #271

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

Merged
merged 18 commits into from
Aug 17, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions .github/workflows/panvimdoc.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
name: panvimdoc

on:
pull_request:
push:
branches-ignore:
- 'main'

permissions:
contents: write
Expand Down Expand Up @@ -64,7 +66,8 @@ jobs:
shiftheadinglevelby: 0 # Shift heading levels by specified number
incrementheadinglevelby: 0 # Increment heading levels by specified number

- uses: stefanzweifel/git-auto-commit-action@v4
- uses: stefanzweifel/git-auto-commit-action@v6.0.1
with:
commit_message: "Auto generate docs"
branch: ${{ github.head_ref }}
file_pattern: 'doc/*.txt'
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
.PHONY: multitest

DEFAULT_GROUPS=--group dev --group lsp --group mcp
DEFAULT_GROUPS=--group dev --group lsp --group mcp --group debug

deps:
pdm lock $(DEFAULT_GROUPS) || pdm lock $(DEFAULT_GROUPS) --group legacy; \
Expand Down
20 changes: 20 additions & 0 deletions doc/VectorCode-cli.txt
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ Table of Contents *VectorCode-cli-table-of-contents*
- |VectorCode-cli-cleaning-up|
- |VectorCode-cli-inspecting-and-manupulating-files-in-an-indexed-project|
- |VectorCode-cli-debugging-and-diagnosing|
- |VectorCode-cli-profiling|
- |VectorCode-cli-post-mortem-debugging|
- |VectorCode-cli-shell-completion|
- |VectorCode-cli-hardware-acceleration|
- |VectorCode-cli-for-developers|
Expand Down Expand Up @@ -605,6 +607,24 @@ For example:
Depending on the MCP/LSP client implementation, you may need to take extra
steps to make sure the environment variables are captured by VectorCode.

PROFILING

When you pass `--debug` parameter to the CLI, VectorCode will track the call
stacks with cprofile <https://docs.python.org/3/library/profile.html>. The
stats will be saved to the log directory mentioned above. You may use an
external stats viewer (like snakeviz <https://jiffyclub.github.io/snakeviz/>)
to load the profiling stats for a better viewing experience.


POST-MORTEM DEBUGGING

VectorCode can work with coredumpy
<https://github.com/gaogaotiantian/coredumpy> to snapshot an exception so that
developers can inspect the error asynchronously. To use this, you’d need to
install the `vectorcode[debug]` dependency group: `uv tool install
vectorcode[debug]`.


SHELL COMPLETION*VectorCode-cli-vectorcode-command-line-tool-shell-completion*

VectorCode supports shell completion for bash/zsh/tcsh. You can use `vectorcode
Expand Down
17 changes: 17 additions & 0 deletions docs/cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@
* [Cleaning up](#cleaning-up)
* [Inspecting and Manupulating Files in an Indexed Project](#inspecting-and-manupulating-files-in-an-indexed-project)
* [Debugging and Diagnosing](#debugging-and-diagnosing)
* [Profiling](#profiling)
* [Post-mortem debugging](#post-mortem-debugging)
* [Shell Completion](#shell-completion)
* [Hardware Acceleration](#hardware-acceleration)
* [For Developers](#for-developers)
Expand Down Expand Up @@ -551,6 +553,21 @@ VECTORCODE_LOG_LEVEL=INFO vectorcode vectorise file1.py file2.lua
> Depending on the MCP/LSP client implementation, you may need to take extra
> steps to make sure the environment variables are captured by VectorCode.

#### Profiling

When you pass `--debug` parameter to the CLI, VectorCode will track the call
stacks with [cprofile](https://docs.python.org/3/library/profile.html). The
stats will be saved to the log directory mentioned above. You may use an
external stats viewer (like [snakeviz](https://jiffyclub.github.io/snakeviz/))
to load the profiling stats for a better viewing experience.

#### Post-mortem debugging

VectorCode can work with [coredumpy](https://github.com/gaogaotiantian/coredumpy)
to snapshot an exception so that developers can inspect the error
asynchronously. To use this, you'd need to install the `vectorcode[debug]`
dependency group: `uv tool install vectorcode[debug]`.

## Shell Completion

VectorCode supports shell completion for bash/zsh/tcsh. You can use `vectorcode -s {bash,zsh,tcsh}`
Expand Down
4 changes: 2 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ omit = [
"./tests/*",
"src/vectorcode/_version.py",
"src/vectorcode/__init__.py",
"src/vectorcode/debugging.py",
"/tmp/*",
]
include = ['src/vectorcode/**/*.py']
Expand All @@ -63,14 +64,12 @@ write_template = "__version__ = '{}' # pragma: no cover"
dev = [
"ipython>=8.31.0",
"ruff>=0.9.1",
"viztracer>=1.0.0",
"pre-commit>=4.0.1",
"pytest>=8.3.4",
"pdm-backend>=2.4.3",
"coverage>=7.6.12",
"pytest-asyncio>=0.25.3",
"debugpy>=1.8.12",
"coredumpy>=0.4.1",
"basedpyright>=1.29.2",
]

Expand All @@ -79,6 +78,7 @@ legacy = ["numpy<2.0.0", "torch==2.2.2", "transformers<=4.49.0"]
intel = ['optimum[openvino]', 'openvino']
lsp = ['pygls<2.0.0', 'lsprotocol']
mcp = ['mcp<2.0.0', 'pydantic']
debug = ["coredumpy>=0.4.1"]

[tool.basedpyright]
typeCheckingMode = "standard"
Expand Down
8 changes: 8 additions & 0 deletions src/vectorcode/cli_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ class FilesAction(StrEnum):

@dataclass
class Config:
debug: bool = False
no_stderr: bool = False
recursive: bool = False
include_hidden: bool = False
Expand Down Expand Up @@ -198,6 +199,12 @@ async def merge_from(self, other: "Config") -> "Config":
def get_cli_parser():
__default_config = Config()
shared_parser = argparse.ArgumentParser(add_help=False)
shared_parser.add_argument(
"--debug",
default=False,
action="store_true",
help="Enable debug mode.",
)
chunking_parser = argparse.ArgumentParser(add_help=False)
chunking_parser.add_argument(
"--overlap",
Expand Down Expand Up @@ -423,6 +430,7 @@ async def parse_cli_args(args: Optional[Sequence[str]] = None):
"action": CliAction(main_args.action),
"project_root": main_args.project_root,
"pipe": main_args.pipe,
"debug": main_args.debug,
}

match main_args.action:
Expand Down
65 changes: 65 additions & 0 deletions src/vectorcode/debugging.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import atexit
import cProfile
import logging
import os
import pstats
from datetime import datetime

__LOG_DIR = os.path.expanduser("~/.local/share/vectorcode/logs/")

logger = logging.getLogger(name=__name__)

__profiler: cProfile.Profile | None = None


def _ensure_log_dir():
"""Ensure the log directory exists"""
os.makedirs(__LOG_DIR, exist_ok=True)


def finish():
"""Clean up profiling and save results"""
if __profiler is not None:
try:
__profiler.disable()
stats_file = os.path.join(
__LOG_DIR,
f"cprofile-{datetime.now().strftime('%Y-%m-%d_%H-%M-%S')}.stats",
)
__profiler.dump_stats(stats_file)
print(f"cProfile stats saved to: {stats_file}")

# Print summary stats
stats = pstats.Stats(__profiler)
stats.sort_stats("cumulative")
stats.print_stats(20)
except Exception as e:
logger.warning(f"Failed to save cProfile output: {e}")


def enable():
"""Enable cProfile-based profiling and crash debugging"""
global __profiler

try:
_ensure_log_dir()

# Initialize cProfile for comprehensive profiling
__profiler = cProfile.Profile()
__profiler.enable()
atexit.register(finish)
logger.info("cProfile profiling enabled successfully")

try:
import coredumpy # noqa: F401

logger.info("coredumpy crash debugging enabled successfully")
coredumpy.patch_except(directory=__LOG_DIR)
except Exception as e:
logger.warning(
f"Crash debugging will not be available. Failed to import coredumpy: {e}"
)

except Exception as e:
logger.error(f"Failed to initialize cProfile: {e}")
logger.warning("Profiling will not be available for this session")
6 changes: 6 additions & 0 deletions src/vectorcode/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,12 @@ async def async_main():
cli_args = await parse_cli_args()
if cli_args.no_stderr:
sys.stderr = open(os.devnull, "w")

if cli_args.debug:
from vectorcode import debugging

debugging.enable()

logger.info("Collected CLI arguments: %s", cli_args)

if cli_args.project_root is None:
Expand Down
Loading