.cursor/skills/jmeter-debugging/SKILL.md
Iterative AI-driven JMeter script debugging via smoke tests, log analysis, and targeted fixes. Use when the user asks to debug, troubleshoot, fix errors in, or validate a JMeter script, or mentions JMeter smoke testing failures.
npx skillsauth add canyonlabz/mcp-perf-suite jmeter-debuggingInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
This section provides context for humans and capable models. For the step-by-step execution instructions, skip to the Execution section below.
This workflow mirrors how an experienced performance test engineer debugs a script:
Maximum 5 iterations. If not resolved after 5 cycles, stop and report.
Missing or stale correlation values (NOT_FOUND in request body or URL):
Extractor on the wrong sampler (common in OAuth/SSO flows):
Wrong request body or parameterization:
edit_jmeter_component with replace_in_bodyAuth token or session issues:
Server-side validation errors (application error in response body):
A markdown file maintained throughout debugging to log all issues and fixes.
Location: artifacts/{test_run_id}/analysis/debug_manifest.md
The manifest is created at the start, appended after each iteration, and finalized when debugging completes. See the Execution section for the exact templates.
This workflow integrates with the PerfMemory lessons-learned memory layer. Before
starting a debug loop, the agent searches for similar past issues. During debugging,
each attempt is stored so future agents can learn from it. See the perfmemory skill
for full details on PerfMemory tools and workflows.
Memory is advisory — if the PerfMemory MCP server is unavailable, skip all memory-related steps and proceed with normal debugging. Do not block debugging because memory is down.
prerequisites.mdc — test_run_id and artifact structure validationskill-execution-rules.mdc — Follow steps in order, do not skipmcp-error-handling.mdc — MCP tool error handlingjmeter-script-guardrails.mdc — Smoke test = 1/1/1, one fix at a time, max 5 iterations, stop on environment issuesThis is an iterative workflow. Steps 3-8 repeat until the script is clean or the iteration limit (5) is reached.
Ask the user for the following values. Do not proceed until all required values are collected.
REQUIRED:
test_run_id = [ask user — must have an existing JMX in artifacts/{test_run_id}/jmeter/]
Input: test_run_id
Action: Before starting the debug loop, check if this system has known issues in memory. This is a broad check to pre-load context — specific symptom searches happen at Step 5.
0.5a. Search for past sessions related to this system:
find_similar_attempts(
symptom_text = "JMeter script debugging for {system_under_test}",
system_under_test = {system_under_test}
)
0.5b. If results are returned (any similarity), note the top matches for context. These give the agent awareness of what types of issues have been seen before on this system (e.g., "this system commonly has OAuth correlation issues").
0.5c. If no results or PerfMemory is unavailable, proceed normally — this step is advisory only.
Save: memory_context (list of top match summaries, or empty)
Input: test_run_id
Action: Create the file artifacts/{test_run_id}/analysis/debug_manifest.md with:
# Debug Manifest
- **Test Run ID**: {test_run_id}
- **Script**: {jmx_filename}
- **Started**: {current timestamp YYYY-MM-DD HH:MM:SS}
- **Status**: In Progress
---
Save: iteration_count = 0
Input: test_run_id, jmx_filename
Action: Open a debug session in PerfMemory to track this debugging effort.
store_debug_session(
system_under_test = {system_under_test},
test_run_id = {test_run_id},
script_name = {jmx_filename},
auth_flow_type = {if known from script analysis, otherwise omit},
environment = {if known, otherwise omit},
created_by = "cursor"
)
Save: pm_session_id from the response.
If PerfMemory is unavailable, set pm_session_id = null and skip all subsequent
PerfMemory steps.
Input: test_run_id
Action: Call analyze_jmeter_script to find the Thread Group node:
analyze_jmeter_script(
test_run_id = {test_run_id},
detail_level = "detailed"
)
Check the Thread Group properties. If not set to 1/1/1, override:
edit_jmeter_component(
test_run_id = {test_run_id},
target_node_id = {thread_group_node_id},
operations = [
{"op": "set_prop", "name": "ThreadGroup.num_threads", "value": "1"},
{"op": "set_prop", "name": "ThreadGroup.ramp_time", "value": "1"},
{"op": "set_prop", "name": "LoopController.loops", "value": "1"}
],
dry_run = false
)
Input: test_run_id
Action:
3a. List scripts to get the JMX path:
list_jmeter_scripts(
test_run_id = {test_run_id}
)
Save: jmx_path from the response.
3b. Start the test:
start_jmeter_test(
test_run_id = {test_run_id},
jmx_path = {jmx_path}
)
Save: pid from the response.
3c. Monitor and stop early on errors:
get_jmeter_run_status(
test_run_id = {test_run_id},
pid = {pid}
)
Poll every few seconds. When errors appear (error rate > 0%), verify errors in the JTL before stopping:
artifacts/{test_run_id}/jmeter/test-results.csvsuccess column is falsesuccess=false rows exist: Errors are confirmed. Stop the test immediately.success=false rows exist: The error_rate was a transient JTL parsing
artifact (race condition from reading the CSV while JMeter is mid-write). Ignore
the error and continue polling.The metrics.error_rate from get_jmeter_run_status can report false positives
due to this JTL read race condition. Always verify against the JTL source of truth.
Once errors are confirmed in the JTL, stop the test:
stop_jmeter_test(
test_run_id = {test_run_id}
)
After stopping, wait a few seconds for JMeter threads to wind down before proceeding to log analysis. Thread shutdown is not instantaneous.
If the test completes on its own with 0% errors, proceed directly to Step 4.
Fallback: Only kill the PID if stop_jmeter_test fails to stop the test.
Input: test_run_id
Action: Call analyze_jmeter_log with log_source="jmeter" (not the default):
analyze_jmeter_log(
test_run_id = {test_run_id},
log_source = "jmeter"
)
Save: error_rate and first_failing_sampler from the response.
Input: error_rate, first_failing_sampler, log analysis output
Decision gate — choose ONE path:
Path A — Script is clean (0% error rate):
Path B — Environment/systemic issue (stop immediately): If ANY of these are true, stop debugging and go to Step 10 (Report):
Path C — Script issue (continue debugging): If errors are isolated to specific samplers:
5d. Memory-assisted triage — Before proceeding to Step 6, search PerfMemory for
this specific symptom (skip if pm_session_id is null):
find_similar_attempts(
symptom_text = {build structured symptom from the error — see perfmemory skill
for the template},
system_under_test = {system_under_test},
error_category = {error category from log analysis}
)
If recommendation = apply_known_fix (similarity > 0.85):
matched_attempt_id from the top match for Step 8eIf recommendation = review_suggestions (similarity 0.60 - 0.85):
matched_attempt_idIf recommendation = no_match:
Input: test_run_id, first_failing_sampler node_id
Action:
6a. Enable verbose logging:
edit_jmeter_component(
test_run_id = {test_run_id},
target_node_id = {udv_node_id},
operations = [{"op": "set_prop", "name": "VERBOSE_LOGGING", "value": "true"}],
dry_run = false
)
6b. Add the built-in debug post-processor to the first failing sampler:
add_jmeter_component(
test_run_id = {test_run_id},
component_type = "jsr223_debug_postprocessor",
parent_node_id = {first_failing_sampler_node_id},
component_config = {},
dry_run = false
)
Save: debug_postprocessor_node_id from the response.
Input: test_run_id
Action:
7a. Re-run the smoke test (same as Step 3, including early stop on errors).
7b. Run analyze_jmeter_log(test_run_id, log_source="jmeter") again.
7c. Read the raw JMeter log at artifacts/{test_run_id}/jmeter/{test_run_id}.log.
Search for lines containing [ERROR]:[DEBUG]: — these contain the verbose
request/response details from the debug post-processor.
Diagnose the root cause using the debug output. See the Common Diagnosis Patterns in the Reference section.
Input: test_run_id, diagnosis from Step 7
Action:
8a. Apply a single targeted fix using add_jmeter_component or edit_jmeter_component.
Always use dry_run=true first, then dry_run=false to apply.
8b. Re-analyze the script to verify structure:
analyze_jmeter_script(
test_run_id = {test_run_id},
detail_level = "summary"
)
8c. Increment iteration_count = iteration_count + 1
8d. Append to the debug manifest:
## Iteration {iteration_count} — {current timestamp}
### Error Identified
- **Sampler**: {sampler_name}
- **Response Code**: {code}
- **Error Message**: {message}
### Diagnosis
{description of root cause}
### Fix Applied
{description of fix and which component was added/edited}
### Result After Fix
{to be filled after re-test}
Completed at {current timestamp}.
8e. Store attempt in PerfMemory (skip if pm_session_id is null):
store_debug_attempt(
session_id = {pm_session_id},
iteration_number = {iteration_count},
symptom_text = {structured symptom — see perfmemory skill for template},
outcome = {resolved | failed | environment_issue | test_data_issue |
authentication_issue | needs_investigation},
error_category = {from log analysis},
severity = {Critical | High | Medium},
response_code = {HTTP status code or exception},
hostname = {hostname where error occurred},
sampler_name = {failing sampler name},
api_endpoint = {failing URL},
diagnosis = {root cause from Step 7},
fix_description = {what was applied in Step 8a},
fix_type = {add_extractor | move_extractor | edit_request_body |
edit_header | edit_correlation | other},
component_type = {json_extractor | regex_extractor | jsr223_postprocessor |
jsr223_preprocessor | http_sampler | test_plan | other},
matched_attempt_id = {if a memory match was applied, its attempt_id — otherwise omit}
)
Save: last_attempt_id from the response.
8f. Decision gate:
iteration_count >= 5: Go to Step 10 (Report) with status "Iteration Limit Reached".iteration_count < 5: Go back to Step 3 (run a fresh smoke test).Input: test_run_id, debug_postprocessor_node_id(s)
Action:
9a. Disable each debug post-processor (do NOT delete):
edit_jmeter_component(
test_run_id = {test_run_id},
target_node_id = {debug_postprocessor_node_id},
operations = [{"op": "toggle_enabled"}],
dry_run = false
)
9b. Set verbose logging back to false:
edit_jmeter_component(
test_run_id = {test_run_id},
target_node_id = {udv_node_id},
operations = [{"op": "set_prop", "name": "VERBOSE_LOGGING", "value": "false"}],
dry_run = false
)
9c. Run one final 1/1/1 smoke test (same as Step 3) to confirm the script is clean with debug artifacts disabled. This validation run should complete fully since we expect 0% errors. If errors still appear, stop the test early and return to Step 5.
9d. Go to Step 10 (Report) with status "Resolved".
Input: test_run_id, iteration_count, final status
Action:
10a. Update the debug manifest header Status and append:
## Final Summary
- **Started**: {start timestamp}
- **Completed**: {current timestamp}
- **Total Duration**: {elapsed time}
- **Total Iterations**: {iteration_count}
- **Final Status**: {Resolved | Needs Human Intervention | Iteration Limit Reached}
- **Fixes Applied**:
1. {fix description}
2. {fix description}
10b. Close PerfMemory session (skip if pm_session_id is null):
close_debug_session(
session_id = {pm_session_id},
final_outcome = {resolved | unresolved | environment_issue |
test_data_issue | authentication_issue |
iteration_limit_reached | needs_investigation},
resolution_attempt_id = {last_attempt_id if resolved, otherwise omit},
notes = {brief summary of outcome}
)
10c. Tell the user:
artifacts/{test_run_id}/analysis/debug_manifest.mdThese rules apply to every step:
dry_run=true before applying any edit.log_source="jmeter" for local smoke tests (not the default "blazemeter").pm_session_id is null, skip all PerfMemory steps (0.5, 1.5, 5d, 8e, 10b).testing
Orchestrates BlazeMeter and Datadog data extraction using dedicated subagents. Use when the user mentions subagent workflow, subagent orchestrator, or wants to run the BlazeMeter and Datadog extraction phases via subagents. This skill handles the extraction and handoff phases only — it does NOT run PerfAnalysis, PerfReport, or Confluence. After this skill completes, the user can continue with the performance-testing-workflow skill starting from Step 4 (PerfAnalysis).
testing
AI-assisted HITL revision of performance test reports with iterative refinement, version tracking, and optional Confluence publishing. Use when the user mentions report revision, AI-enhanced report, revise executive summary, revise key observations, revise issues table, or improve a performance report.
tools
Run Playwright browser automation to capture network traffic and generate a JMeter JMX script. Use when the user mentions browser automation, Playwright recording, test spec execution, browser-based network capture, or generating a JMeter script from a live browser session.
tools
End-to-end performance testing pipeline orchestrating BlazeMeter, Datadog, PerfAnalysis, PerfReport, and Confluence MCP workflows sequentially. Use when the user mentions performance testing workflow, E2E pipeline, BlazeMeter results, test run analysis, performance report generation, or end-to-end test processing.