SkillAgentSearch skills...

cdpwave-debugging

Bilingual developer knowledge base with 1000+ code recipes, design patterns, architecture guides, and reusable documentation templates. Built with Astro, Tailwind CSS, and Pagefind.

Install / Use

npx skills add MathiasPaulenko/stack-practices-web

Installs into whichever agent you are using.

About this skill
📄

SKILL.md

Installable skill definition

Quality Score

60/100

Supported Platforms

Universal

name: CDPWave Debugging version: 1.0.0 author: Mathias Paulenko Echeverz description: "Advanced debugging with cdpwave. Breakpoints, step debugging, CPU profiling, heap snapshots, code coverage, DOM debugger." tags: [debugging, breakpoints, profiling, heap-snapshot] trigger: When the user asks about debugging with cdpwave, needs breakpoints or step debugging, wants CPU profiling, needs heap snapshots, wants code coverage, or needs DOM debugger.

CDPWave Debugging

Description

Advanced debugging with cdpwave — set breakpoints, step through JavaScript, capture CPU profiles, take heap snapshots, measure code coverage, and use the DOM debugger for event-level debugging.

When to Invoke

  • Setting breakpoints and step debugging JavaScript
  • Capturing and analyzing CPU profiles
  • Taking heap snapshots for memory leak detection
  • Measuring JS and CSS code coverage
  • Setting DOM and event breakpoints
  • Debugging JavaScript execution in automated tests

Prerequisites

  • pip install cdpwave
  • Chrome or Edge launched with --remote-debugging-port=9222
  • Basic familiarity with cdpwave (see cdpwave-testing skill)
  • Python 3.11+ with asyncio

Debugger Domain

The CDP Debugger domain provides full step-debugging capabilities over the DevTools Protocol.

Setting breakpoints

debug_set_breakpoint

Set a breakpoint by URL, function, or script:

import asyncio
from cdpwave import CDPSession

async def set_breakpoint():
    session = await CDPSession.connect("ws://localhost:9222")

    # Enable the Debugger domain
    await session.Debugger.enable()

    # Set breakpoint by URL and line number
    result = await session.Debugger.set_breakpoint_by_url(
        url="https://example.com/app.js",
        line_number=42,
        column_number=0
    )
    breakpoint_id = result["breakpointId"]

    # Set breakpoint by function name
    result = await session.Debugger.set_breakpoint_by_function(
        function_name="handleClick"
    )

    # Set breakpoint by script ID and line
    result = await session.Debugger.set_breakpoint(
        location={
            "scriptId": "script123",
            "lineNumber": 10,
            "columnNumber": 0
        }
    )

    await session.close()

Conditional breakpoints

await session.Debugger.set_breakpoint_by_url(
    url="https://example.com/app.js",
    line_number=42,
    condition="x > 100"
)

Logpoints (breakpoints that log without pausing)

await session.Debugger.set_breakpoint_by_url(
    url="https://example.com/app.js",
    line_number=42,
    condition="console.log('hit line 42, x=', x); false"
)

Pausing and resuming

debug_pause

Pause JavaScript execution:

await session.Debugger.pause()

debug_resume

Resume execution after a pause:

await session.Debugger.resume()

Step debugging

| Method | Description | |--------|-------------| | Debugger.step_over | Step over the next function call | | Debugger.step_into | Step into the next function call | | Debugger.step_out | Step out of the current function | | Debugger.resume | Resume execution until next breakpoint |

async def step_debug(session):
    # Wait for pause event
    paused = await session.Debugger.wait_for_pause()

    print(f"Paused at: {paused['callFrames'][0]['url']}")
    print(f"Line: {paused['callFrames'][0]['location']['lineNumber']}")

    # Step over
    await session.Debugger.step_over()
    paused = await session.Debugger.wait_for_pause()

    # Step into
    await session.Debugger.step_into()
    paused = await session.Debugger.wait_for_pause()

    # Step out
    await session.Debugger.step_out()
    paused = await session.Debugger.wait_for_pause()

    # Resume
    await session.Debugger.resume()

Removing breakpoints

# Remove a specific breakpoint
await session.Debugger.remove_breakpoint(breakpoint_id=breakpoint_id)

# Remove all breakpoints
await session.Debugger.set_skip_all_breakpoints(skip=True)

Call frames and scope

When paused, inspect call frames and scope chains:

async def inspect_pause(session):
    paused = await session.Debugger.wait_for_pause()

    for frame in paused["callFrames"]:
        print(f"Function: {frame['functionName']}")
        print(f"URL: {frame['url']}")
        print(f"Line: {frame['location']['lineNumber']}")

        for scope in frame["scopeChain"]:
            print(f"  Scope: {scope['type']}")  # global, local, closure, catch, block, script, with
            obj = await session.Runtime.get_properties(
                object_id=scope["object"]["objectId"],
                own_properties=True
            )
            for prop in obj["result"]:
                print(f"    {prop['name']} = {prop.get('value', {}).get('value', 'N/A')}")

HeapProfiler Domain

Capture heap snapshots for memory leak detection and analysis.

Taking a heap snapshot

async def heap_snapshot(session):
    await session.HeapProfiler.enable()

    # Capture heap snapshot
    snapshot_data = []
    async for event in session.HeapProfiler.take_heap_snapshot():
        snapshot_data.append(event)

    # Save snapshot
    with open("heap.heapsnapshot", "w") as f:
        json.dump(snapshot_data, f)

Heap sampling

async def heap_sampling(session):
    await session.HeapProfiler.enable()

    # Start sampling
    await session.HeapProfiler.start_sampling()

    # ... run page interactions ...

    # Stop sampling and get results
    profile = await session.HeapProfiler.stop_sampling()

    with open("heap-sampling.json", "w") as f:
        json.dump(profile, f)

Tracking heap object allocations

async def track_allocations(session):
    await session.HeapProfiler.enable()

    # Start tracking
    await session.HeapProfiler.start_tracking_heap_objects()

    # ... run page interactions ...

    # Stop tracking and get snapshot
    await session.HeapProfiler.stop_tracking_heap_objects()

    # Get tracked objects
    objects = await session.HeapProfiler.get_heap_object_id()

Heap snapshot analysis

import json

def analyze_heap(snapshot_path):
    with open(snapshot_path) as f:
        snapshot = json.load(f)

    nodes = snapshot["nodes"]
    strings = snapshot["strings"]

    # Count nodes by type
    type_counts = {}
    for i in range(0, len(nodes), 7):
        type_idx = nodes[i]
        type_name = strings[type_idx]
        type_counts[type_name] = type_counts.get(type_name, 0) + 1

    for type_name, count in sorted(type_counts.items(), key=lambda x: -x[1]):
        print(f"{type_name}: {count}")

Profiler Domain

Capture CPU profiles to identify performance bottlenecks.

CPU profiling

async def cpu_profile(session):
    await session.Profiler.enable()

    # Start profiling
    await session.Profiler.start()

    # ... run page interactions ...

    # Stop profiling and get profile
    profile = await session.Profiler.stop()

    with open("cpu-profile.json", "w") as f:
        json.dump(profile, f)

Profiling with precision

async def precise_profile(session):
    await session.Profiler.enable()

    # Set sampling interval (default is 1000us = 1ms)
    await session.Profiler.set_sampling_interval(interval=100)  # 100us = 0.1ms

    await session.Profiler.start()

    # ... run interactions ...

    profile = await session.Profiler.stop()

CPU profile analysis

import json

def analyze_cpu_profile(profile_path):
    with open(profile_path) as f:
        profile = json.load(f)

    nodes = profile["profile"]["nodes"]
    samples = profile["profile"]["samples"]
    time_deltas = profile["profile"]["timeDeltas"]

    # Find hot functions (most samples)
    sample_counts = {}
    for sample in samples:
        sample_counts[sample] = sample_counts.get(sample, 0) + 1

    # Map node IDs to function names
    node_map = {n["id"]: n for n in nodes}

    for node_id, count in sorted(sample_counts.items(), key=lambda x: -x[1])[:10]:
        node = node_map[node_id]
        func = node["callFrame"]["functionName"] or "(anonymous)"
        url = node["callFrame"]["url"]
        line = node["callFrame"]["lineNumber"]
        print(f"{func} at {url}:{line} — {count} samples")

DOMDebugger Domain

Set breakpoints on DOM events and DOM modifications.

Event breakpoints

async def event_breakpoints(session):
    await session.DOMDebugger.enable()

    # Break on all click events
    await session.DOMDebugger.set_event_breakpoint(
        event_name="click"
    )

    # Break on XHR load
    await session.DOMDebugger.set_event_breakpoint(
        event_name="load",
        target_name="XMLHttpRequest"
    )

    # Break on specific event
    await session.DOMDebugger.set_event_breakpoint(
        event_name="submit"
    )

DOM breakpoints

async def dom_breakpoints(session):
    await session.DOMDebugger.enable()

    # Get a DOM node first
    document = await session.DOM.get_document()
    node_id = document["root"]["children"][1]["children"][0]["nodeId"]

    # Break on subtree modification
    await session.DOMDebugger.set_dom_breakpoint(
        node_id=node_id,
        type="subtree-modified"  # subtree-modified, attribute-modified, node-removed
    )

    # Break on attribute modification
    await session.DOMDebugger.set_dom_breakpoint(
        node_id=node_id,
        type="attribute-modified"
    )

    # Break on node removal
    await session.DOMDebugger.set_dom_breakpoint(
        node_id=node_id,
        type="node-removed"
    )

Removing DOM breakpoints

# Remove specific DOM breakpoint
await session.DOMDebugger.remove_dom_breakpoint(
    node_id=node_id,
    type="subtree-modified"
)

# Remove event breakpoint
await session.DOMDebugger.remove_event_breakpoint(
    event_name="click"
)

Code Coverage

Measure JS and CSS code coverage to find unused code.

JS coverage

async def js_coverage(session):
    await session.Profiler.enable()
    await session.Profiler.start_precise_coverage(
        call_count=True,
        detailed=True
    )

    # ... run page interactions ...

    # Get coverage results
    result = await session.Profiler.take_precise_coverage()

    for script in result["result"]:
        url = script["url"]
        functions = script["functions"]
        for func in functions:
            func_name = func["functionName"] or "(anonymous)"
            for range in func["ranges"]:
                start = range["startOffset"]
                end = range["endOffset"]
                count = range["count"]
                if count == 0:
                    print(f"UNUSED: {func_name} in {url} [{start}:{end}]")

    await session.Profiler.stop_precise_coverage()

CSS coverage

async def css_coverage(session):
    await session.CSS.enable()
    await session.CSS.start_rule_usage_tracking()

    # ... run page interactions ...

    # Get coverage results
    result = await session.CSS.stop_rule_usage_tracking()

    for rule in result["ruleUsage"]:
        style_sheet_id = rule["styleSheetId"]
        start = rule["startOffset"]
        end = rule["endOffset"]
        used = rule["used"]
        if not used:
            print(f"UNUSED CSS: sheet={style_sheet_id} [{start}:{end}]")

Combined Debug Session

A complete debug session combining breakpoints, profiling, and heap analysis:

import asyncio
import json
from cdpwave import CDPSession

async def debug_session():
    session = await CDPSession.connect("ws://localhost:9222")

    # Enable all required domains
    await session.Debugger.enable()
    await session.Profiler.enable()
    await session.HeapProfiler.enable()

    # Set breakpoint
    bp = await session.Debugger.set_breakpoint_by_url(
        url="https://example.com/app.js",
        line_number=42,
        con

Truncated for display — read the full file on GitHub.

Related Skills

View on GitHub
GitHub Stars0
CategoryContent
UpdatedNaNy ago
Forks0

Security Score

68/100

Audited on Invalid Date

2 medium1 low