JDWP/JDI aracılığıyla çalışan Java uygulamaları üzerinde durum inceleme ve değer değiştirme dahil olmak üzere yapay zeka ajanlarına tam hata ayıklama (debugger) kontrolü sağlar.
Ayrıca bilinen adı FgForrest/mcp-jdwp-java
- Yıldızlar
- 34
- Araçlar
- 50
- İndirmeler, 30 gün
- —
- En son sürüm
- —
- Son commit
- 14 Kas 2026
- Lisans
- MIT
jdwp-java MCP nedir?
jdwp-java bir MCP sunucusudur. JDWP/JDI aracılığıyla çalışan Java uygulamaları üzerinde durum inceleme ve değer değiştirme dahil olmak üzere yapay zeka ajanlarına tam hata ayıklama (debugger) kontrolü sağlar. 50 araç sunar; örneğin jdwp_connect, jdwp_disconnect, jdwp_reconnect.
Özet, projenin README dosyasından oluşturuldu
jdwp-java MCP sunucusu hakkında
Bu MCP server, çalışan Java uygulamalarını bir yapay zeka ajanı içerisinden hata ayıklamanıza olanak tanır. Ajanların JDWP ve JDI protokollerini kullanarak bir JVM üzerinde derinlemesine inceleme ve çalışma zamanı manipülasyonu yapabilmesi için bir arayüz sağlar. Bir ajan sadece stack trace okumakla kalmaz, tüm nesne grafiğini keşfedebilir, yerel değişkenleri okuyabilir, alanları veya thread'leri inceleyebilir. Bu, IntelliJ veya Eclipse gibi profesyonel IDE'lere benzer bir tanısal derinlik sunar.
Sunucu, STDIO üzerinden yerel olarak çalışır ve hedef bir JVM'e yerel bir JDWP soketi üzerinden bağlanır. Çalıştırmak için JDK 17 veya üzeri gereklidir, ancak sunucuyu kaynak koddan derlemek için JDK 21 gereklidir. Kullanıcılar bunu bir eklenti veya manuel kayıt kullanarak Claude Code ile entegre edebilirler. Bağlantı kurulduktan sonra ajan; koşullu breakpoint'ler ayarlamak, rastgele Java ifadelerini değerlendirmek ve hatta uygulamayı yeniden başlatmadan farklı senaryoları test etmek için alan değerlerini veya yerel değişkenleri değiştirmek amacıyla 50'den fazla araç kullanabilir.
Neler yapabilirsiniz
- 1Çalışma zamanı nesne durumunu incelemeBir ajan, belirli bir değerin neden hatalı olduğunu anlamak için tüm nesne grafiğini inceleyebilir ve yerel değişkenleri okuyabilir.
- 2Çalışma zamanı manipülasyonu ile mantık testiBir geliştirici, uygulamanın nasıl tepki verdiğini görmek için program duraklatılmışken bir alan değerini veya yerel bir değişkeni değiştirmesini bir yapay zekadan isteyebilir.
- 3Watchpoint'ler ile alan değişikliklerini izlemeBir geliştirici, çalışan uygulama tarafından belirli bir alanın tam olarak ne zaman ve nerede değiştirildiğini takip edebilir.
Projenin README dosyasından alınmıştır. Her kullanım durumu, oradaki bir alıntı ile kontrol edilir.
jdwp-java MCP araçları 50
README dosyasından algılandıjdwp_connectjdwp_disconnectjdwp_reconnectjdwp_wait_for_attachjdwp_get_versionjdwp_get_threadsjdwp_dump_locksjdwp_get_stackjdwp_get_localsjdwp_get_fieldsjdwp_to_stringjdwp_get_breakpoint_context
| jdwp_connect | — | |
| jdwp_disconnect | — | |
| jdwp_reconnect | — | |
| jdwp_wait_for_attach | — | |
| jdwp_get_version | — | |
| jdwp_get_threads | — | |
| jdwp_dump_locks | — | |
| jdwp_get_stack | — | |
| jdwp_get_locals | — | |
| jdwp_get_fields | — | |
| jdwp_to_string | — | |
| jdwp_get_breakpoint_context | — |
50 aracın tümünü gösterDaha az araç göster
jdwp_get_current_threadjdwp_resumejdwp_resume_threadjdwp_suspend_threadjdwp_resume_until_eventjdwp_step_overjdwp_step_intojdwp_step_outjdwp_set_breakpointjdwp_set_logpointjdwp_set_fast_logpointjdwp_set_fast_breakpointjdwp_restore_fast_logpointsjdwp_clear_breakpointjdwp_set_exception_breakpointjdwp_set_exception_logpointjdwp_set_field_breakpointjdwp_set_field_logpointjdwp_set_breakpoint_dependencyjdwp_clear_breakpoint_dependencyjdwp_disarm_until_triggerjdwp_evaluate_expressionjdwp_assert_expressionjdwp_set_localjdwp_set_fieldjdwp_get_eventsjdwp_clear_eventsjdwp_diagnosejdwp_attach_watcherjdwp_detach_watcherjdwp_list_watchers_for_breakpointjdwp_evaluate_watchersjdwp_mark_instancejdwp_unmark_instancejdwp_rename_markjdwp_overviewjdwp_clearjdwp_reset
| jdwp_get_current_thread | — | |
| jdwp_resume | — | |
| jdwp_resume_thread | — | |
| jdwp_suspend_thread | — | |
| jdwp_resume_until_event | — | |
| jdwp_step_over | — | |
| jdwp_step_into | — | |
| jdwp_step_out | — | |
| jdwp_set_breakpoint | — | |
| jdwp_set_logpoint | — | |
| jdwp_set_fast_logpoint | — | |
| jdwp_set_fast_breakpoint | — | |
| jdwp_restore_fast_logpoints | — | |
| jdwp_clear_breakpoint | — | |
| jdwp_set_exception_breakpoint | — | |
| jdwp_set_exception_logpoint | — | |
| jdwp_set_field_breakpoint | — | |
| jdwp_set_field_logpoint | — | |
| jdwp_set_breakpoint_dependency | — | |
| jdwp_clear_breakpoint_dependency | — | |
| jdwp_disarm_until_trigger | — | |
| jdwp_evaluate_expression | — | |
| jdwp_assert_expression | — | |
| jdwp_set_local | — | |
| jdwp_set_field | — | |
| jdwp_get_events | — | |
| jdwp_clear_events | — | |
| jdwp_diagnose | — | |
| jdwp_attach_watcher | — | |
| jdwp_detach_watcher | — | |
| jdwp_list_watchers_for_breakpoint | — | |
| jdwp_evaluate_watchers | — | |
| jdwp_mark_instance | — | |
| jdwp_unmark_instance | — | |
| jdwp_rename_mark | — | |
| jdwp_overview | — | |
| jdwp_clear | — | |
| jdwp_reset | — |
Yapılandırma
MCP_TIMEOUTMCP_TOOL_TIMEOUT
| Ad | Açıklama | Sunucu |
|---|---|---|
| MCP_TIMEOUT | — | |
| MCP_TOOL_TIMEOUT | — |
Otomatik kontroller
Çalıştırıldı 13 Kas 2026Tam rapor →Otomatik kontrollerimizden geçti. Bu bir güvenlik denetimi değildir: kodu okuyun ve sunucuya ihtiyaç duyduğu en dar erişimi verin. Kontroller nasıl çalışır
- Kaynak deposu bağlantılı ve erişilebilirGeçti
- Açık kaynak lisansı (OSI)Geçti
- Son bir yıl içinde commit veya sürüm mevcutGeçti
- Paket adı popüler bir pakete yakın değilKontrol edilmedi
- Uzak uç nokta HTTPS kullanıyorKontrol edilmedi
- Yıkıcı araçlar etiketlenmişGeçti
- Modele yönelik gizli talimat yokGeçti
- Pakette veya bağımlılıklarında kritik uyarı yokKontrol edilmedi
MCP JDWP Inspector
MCP server that gives AI agents full debugger control over running Java applications — inspect state, set breakpoints, evaluate expressions, and mutate values at runtime via JDWP/JDI.
Release notes — see
CHANGELOG.md. 2.0.0 is a breaking release: severaljdwp_clear_*tools were unified, and field watchpoints are new.
Built on the foundations of mcp-jdwp-java by Nicolas Vautrin — the original project that provided core JDI connectivity, thread/stack/variable inspection, stepping, and basic breakpoint management. Everything described below as "beyond standard JDWP" was built on top of that base.
What you get (TL;DR)
An MCP server that speaks JDWP — the same protocol IntelliJ / Eclipse / VS Code use to attach — and exposes the debugger to Claude Code as 50 tools + 2 MCP resources over STDIO. Once the agent attaches to your JVM, it can:
- See runtime state, not stack traces — read locals, fields, threads, the entire object graph
- Evaluate arbitrary Java in the suspended frame — compiled to real bytecode, full classpath, including private/package-private members
- Mutate the running program — set locals, write fields, test "what if?" without restart
- Stop on the bad case only — conditional breakpoints, exception breakpoints at the throw site, deferred BPs that arm before the class loads, chains that gate one BP on another
- Trace without stopping — line/exception/field logpoints write to an event log, the thread runs on
- Catch the writer of a mutating field — watchpoints suspend at every bytecode store to the field, wherever it is; a write through reflection or
Unsafeis the one they cannot see, and the[VM_DEATH]reply's hit count shows when a watchpoint stayed silent - Pure STDIO — no daemon, no extra port, no GUI
The agent gets the same power as IntelliJ's debugger, with extras that exist specifically because an agent isn't a human staring at a UI (filtering, blocking resume, one-shot context dumps, recursion guards, reconnect after a wedge).
Security & trust
This MCP server runs entirely on your local machine:
- No network calls — the server communicates with Claude Code over STDIO and with the target JVM over a local JDWP socket. It makes zero outbound HTTP/internet requests. No telemetry, no analytics, no phone-home.
- Built from source — the JAR is compiled locally on your machine from the source code in this repository (either via the plugin auto-build hook or manually with
./mvnw clean package). No pre-built binaries are downloaded or distributed. - Auditable — the full source is here. The server is a standard Spring Boot application with no obfuscation or native code. Try asking Claude Code itself: "audit these sources for anything that touches network or filesystem beyond the JVM target".
Quick start
Prerequisites
- JDK 17+ on PATH to run the server (must be a JDK, not a JRE — JDI lives in
jdk.jdi). - JDK 21+ to build from source — the build toolchain pins to JDK ≥ 21 because Error Prone 2.48 ships Java-21 bytecode. The bytecode target stays at Java 17, so the resulting JAR still runs on a JDK 17 runtime.
No separate Maven install is required — the repository ships with the Maven Wrapper (./mvnw), which downloads a pinned Maven 3.9.x into ~/.m2/wrapper/ on first use. Every build command in this README uses the wrapper; the SessionStart hook does too, and falls back to mvn on PATH when the wrapper cannot set itself up.
1. Install the plugin
Option A: Plugin marketplace (recommended)
Installs the MCP server, the java-debug skill (debugging workflows, recipes, gotchas), and the MCP server registration in one step:
/plugin marketplace add https://github.com/FgForrest/mcp-jdwp-java.git
/plugin install jdwp-debugging@mcp-jdwp-java
The server JAR is built automatically on first session start via the bundled ./mvnw wrapper (requires JDK 21+ — no Maven install needed). The hook is content-aware: it rebuilds when the JAR is missing or, in a git checkout, when HEAD moves, so git pull + restart picks up updates without manual intervention. When the wrapper cannot set itself up (for example, ~/.m2 is read-only in a sandbox), the hook tries mvn -o and then mvn from PATH. If every attempt fails, a warning at session start names the build log and the exact command to build by hand. A build runs while Claude Code starts the MCP server and the JAR is absent until it finishes, so after a build (first install, update, moved HEAD) jdwp-inspector may show as failed in /mcp; reconnect it there once the build is done. Restart Claude Code to pick up the plugin.
If you prefer to register the MCP server directly without the plugin (no skill, no auto-build):
1. Build the JAR:
git clone https://github.com/FgForrest/mcp-jdwp-java.git
cd mcp-jdwp-java
./mvnw clean package -DskipTests # use mvnw.cmd on Windows
2. Register with Claude Code:
claude mcp add jdwp-inspector -s user \
-e MCP_TIMEOUT=30000 \
-e MCP_TOOL_TIMEOUT=120000 \
-- java --add-modules jdk.jdi,jdk.attach -jar /path/to/mcp-jdwp-java.jar
To change the JDWP port (default 5005), add -DJVM_JDWP_PORT=12345 before -jar.
The MCP_TIMEOUT and MCP_TOOL_TIMEOUT environment variables are important — JVM startup is not instant (class loading, Spring context initialization), so the default MCP timeouts will cause Claude Code to give up before the server is ready. MCP_TIMEOUT=30000 gives the server 30 seconds to start, and MCP_TOOL_TIMEOUT=120000 allows up to 2 minutes for long-running tools like first-time expression evaluation (which discovers the target's classpath and compiles bytecode).
Re-installing requires removing first: claude mcp remove jdwp-inspector -s user
Drop -s user to scope to the current project only.
.mcp.json:
{
"mcpServers": {
"jdwp-inspector": {
"command": "java",
"args": [
"--add-modules", "jdk.jdi,jdk.attach",
"-jar", "/path/to/mcp-jdwp-java.jar"
],
"env": {
"MCP_TIMEOUT": "30000",
"MCP_TOOL_TIMEOUT": "120000"
}
}
}
}
The plugin-managed install also passes -DLOG_PATH=${CLAUDE_PLUGIN_ROOT}/logs/mcp-jdwp-inspector.log so the server's SLF4J log lands inside the plugin directory; add the same flag if you want a fixed log path here.
2. Launch your Java application with JDWP
Maven Surefire (test debugging):
mvn test -Dmaven.surefire.debug
Starts the JVM with JDWP on port 5005, suspended until a debugger connects.
Any Java application:
-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:5005
3. Attach from Claude Code
From there, just tell the agent what to do — "attach to JDWP, set a breakpoint in OrderService.createOrder line 42, run the failing test, dump the context when it hits". The bundled java-debug skill teaches the agent the right tool sequence; the next section gives you a smoke test that exercises the surface.
Find the Bug — test flights
The jdwp-sandbox module ships 10 deliberately broken Java classes. Each one compiles fine, looks reasonable at first glance, and fails its test with a confusing message. Your job: attach with the JDWP MCP server and find the root cause.
This doubles as a setup verification — if you can solve these, everything works.
Each flight is built so that one tool group is the path of least resistance, and lists a par — the minimum tool calls that cleanly reveal the root cause. Hitting par is the elegant solve; the suite as a whole exercises the full tool surface (expression eval, exception breakpoints, field watchpoints, event history, marks, logpoints, runtime mutation, multi-thread inspection, and in-target probes).
Just installed the plugin? Grab the sandbox zip
The fastest path for fresh plugin users is a self-contained zip — no clone, no reactor build:
curl -L -o jdwp-sandbox.zip \
https://github.com/FgForrest/mcp-jdwp-java/releases/latest/download/jdwp-sandbox.zip
unzip jdwp-sandbox.zip && cd jdwp-sandbox
Inside, you get a parentless Maven project with src/, a pom.xml, a flight-game README.md, and a CLAUDE.md that briefs the agent on the game's house rules. Open the folder in Claude Code and ask it to play flight #1 — the bundled CLAUDE.md keeps it honest (no peeking at source, no spoiler-fetching). Continue with the workflow below.
How to launch a test flight
Start Claude Code from the sandbox folder (or this repo's root, if you cloned) and type:
Use JDWP to debug <TestClass> in the jdwp-sandbox module — the test is failing, find the root cause.
#1 The Phantom Session
Difficulty: Moderate | Test: SessionStoreTest | Package: session | Par: 4 | Exercises: expression eval
Symptom: retrieve() returned null — a session was stored, upgraded, and then... vanished from the map.
UserSession is used as a HashMap key, with both userId and role in hashCode(). upgradeUserRole() calls session.upgradeRole("PREMIUM"), which mutates role — changing the hashCode while the key is still in the map. The entry sits in the old hash bucket; lookups compute the new hash and search the wrong bucket.
Debug path: Breakpoint before and after upgradeRole(). Use jdwp_assert_expression("session.hashCode()", "<value-before>") after the upgrade — MISMATCH confirms the hash drifted.
#2 The Swallowed Exception
Difficulty: Hard | Test: EventBusTest | Package: events | Par: 4 | Exercises: exception breakpoint + trigger gate
Symptom: expected stock < 100 but was 100 and an empty error summary — the order was supposed to reserve inventory, but nothing happened and nobody complained.
OrderEvent narrows the raw quantity through byte (200 → -56), so Inventory.reserve() throws IllegalStateException. EventBus.dispatch() runs each handler on a single-thread executor as a fire-and-forget task — the Future is never inspected, so the exception is captured inside java.util.concurrent.FutureTask.run (a JDK frame) and discarded. No sandbox frame ever holds the throwable, so a breakpoint-context dump has nothing to show, and getErrorSummary() stays empty.
Debug path: Because the exception is caught (by FutureTask), an uncaught-only breakpoint never fires. Set a line BP at the test/dispatch entry as a trigger, then jdwp_set_exception_breakpoint("java.lang.IllegalStateException", caught=true, triggerBreakpointId=<trigger>) so bootstrap exceptions don't drown the signal. The throw site suspends with qty = -56 in the frame's locals.
#3 The Time Traveler's Config
Difficulty: Hard | Test: ConfigurationProviderTest | Package: config | Par: 4 | Exercises: field watchpoint + event history
Symptom: expected timeout=5000 but was 0 — the timeout was set during construction, yet it reads back as the default.
ConfigurationProvider's constructor calls init(5000), so the timeout is correct. Then runMaintenanceSweep() starts a background config-reaper thread that wrongly treats the live instance as stale and calls resetToDefaults(), writing the timeout back to 0. By the time the test reads it, the heap shows 0 and no local holds 5000 — a single context dump looks like the value was never set.
Debug path: jdwp_set_field_breakpoint(className="…config.Configuration", fieldName="timeout", mode="modification"), then jdwp_get_events shows the two stores in order — 0→5000 on the main thread, then 5000→0 on config-reaper. jdwp_get_stack on the second event names the reaper as the clobberer.
#4 The Audit That Lies
Difficulty: Hard | Test: TransferServiceTest | Package: bank | Par: 4 | Exercises: field watchpoint + stack narration
Symptom: expected discrepancy=0 but was non-zero — money is neither created nor destroyed, yet the audit says the books don't balance.
TransferService.transfer() is not atomic: it calls source.withdraw(), then auditService.snapshotBalances(), then destination.deposit(). The snapshot captures the intermediate state where money has left the source but hasn't arrived at the destination — showing a total of 1500 instead of 2000.
Debug path: jdwp_set_field_breakpoint on AuditService.lastTotalSnapshot (modification). It fires mid-transfer with the stack at TransferService.transfer between the debit and the credit; jdwp_get_stack plus a live balance read confirm the captured dip.
#5 The Field That Lies
Difficulty: Hard | Test: UserProfileTest | Package: userprofile | Par: 3 | Exercises: field watchpoint blind spot + before/after bisect
Symptom: expected: <Alice> but was: <alice> — the welcome message rendered correctly, yet the user's stored display name has silently changed casing.
LoginNormalizer.welcomeMessage calls a private canonicalForm helper, which delegates to a static DisplayNameMirror nested class. The mirror uses Field.setAccessible(true) + Field.set(profile, canonical) to write the lower-cased form straight into UserProfile.displayName, bypassing the public setter. From the test's side the formatter looks read-only, and a field watchpoint cannot see the write either: JDI watchpoints fire only on the putfield/getfield bytecodes (and JNI field access). Field.set ends in an Unsafe store straight to memory, which trips no watchpoint, in modification mode or in access mode.
Debug path: The tell is a modification watchpoint that fires once, for the constructor's ordinary assignment, and never again, although the value flips before the assertion: the write does not go through a field store. The par path takes three calls:
jdwp_set_field_breakpoint(className="one.edee.jdwp.sandbox.userprofile.UserProfile", fieldName="displayName", mode="modification"). The class is not loaded yet, so the watchpoint waits for it.jdwp_resume_until_event: the watchpoint fires in theUserProfileconstructor, which stores"Alice".jdwp_resume_until_event: the test fails and its JVM exits. The[VM_DEATH]reply lists the watchpoint with one hit. The only store the JVM saw wrote"Alice", yet the test read"alice", so the write is reflective or goes throughUnsafe. Between the two, the test calls onlywelcomeMessage, on line 24.
Bisecting takes three calls as well and shows where the value flips, though not how: jdwp_set_breakpoint("one.edee.jdwp.sandbox.userprofile.UserProfileTest", 27), just after welcomeMessage, then jdwp_resume_until_event, then jdwp_evaluate_expression("profile.getDisplayName()"), which is already "alice". When the old and new strings could be equal, compare System.identityHashCode(profile.getDisplayName()) instead.
That is the evidence to read LoginNormalizer with: canonicalForm → DisplayNameMirror.mirror → Field.set. To name the writing call from the debugger alone, break at line 24 instead and keep bisecting inside welcomeMessage with jdwp_step_into / jdwp_step_over, comparing the identity after each line: it flips across DisplayNameMirror.mirror(profile, canonical) in canonicalForm. A line BP on UserProfile.setDisplayName never fires — the setter is genuinely unused.
#6 The Doppelgänger Cart
Difficulty: Moderate | Test: CheckoutTest | Package: cart | Par: 6 | Exercises: marked instances ($label)
Symptom: expected 45.0 but was 0.0 — the cart goes through pricing and discounting, yet its total never changes.
Checkout.process runs the cart through validate → price → discount. validate was changed to return a defensive snapshot — return new Cart(cart) — so every later stage mutates the copy. process reassigns its local to the copy as it threads the stages, so no single frame holds both the caller's cart and the copy at once. The test ignores the return value (assuming in-place mutation), so its cart stays at total 0. The copy's fields are identical to the original — only identity differs.
Debug path: Break in the test once the cart exists, jdwp_mark_instance(label="input", objectId=<id>) to pin it, then set a breakpoint in price (or applyDiscount) with condition cart != $input. It fires — the stage is working on a doppelgänger, not the caller's cart.
#7 The Heisenbug Race
Difficulty: Hard | Test: RaceCounterTest | Package: race | Par: 3 | Exercises: logpoints (non-stopping)
Symptom: expected 2 but was 1 — two threads each increment a counter once, but one increment vanishes.
RaceCounter.increment reads the count, waits at a CyclicBarrier so both threads have read before either writes, then writes back read + 1. Both threads read 0 and both write 1 — one increment is lost. A suspending breakpoint serializes the threads and makes you juggle two parked threads to reconstruct what happened.
Debug path: Set a non-suspending jdwp_set_logpoint at the read site logging the thread name and count (or a field logpoint on count). Let the test run, then jdwp_get_events — both racer-1 and racer-2 are recorded reading 0 before either writes, which is the lost update laid out in order.
#8 The Magic Patch
Difficulty: Warm-up | Test: DateParserTest | Package: parser | Par: 4 | Exercises: runtime mutation (set_local)
Symptom: NumberFormatException: For input string: "15 " — a date string that looks right fails to parse.
DateParser.parse splits YYYY-MM-DD and Integer.parseInts each part. The feed delivers "2026-05-15 " with a trailing space, so parseInt("15 ") throws. The parser never trims its input.
Debug path: Four calls. jdwp_set_breakpoint at the top of parse, jdwp_resume_until_event to stop there, jdwp_set_local("input", "2026-05-15") to drop the trailing space (or break on the test's line 19 and patch fromFeed instead), and jdwp_resume. The parse now succeeds and the test passes — confirming the trailing space is the whole story, with no rebuild. The exception message already shows the stray space, so the stop needs no inspection call; the confirming resume counts.
#9 The Polite Standoff
Difficulty: Hard | Test: TransferDeadlockTest | Package: deadlock | Par: 4 | Exercises: multi-thread inspection (no breakpoints)
Symptom: The test hangs and then fails its join — both transfers never complete.
Account.transfer locks the source account, pauses, then locks the destination. Two transfers in opposite directions (A→B and B→A) each grab their first lock and then wait forever for the other's — a classic lock-ordering deadlock.
Debug path: No breakpoint can fire — the threads execute nothing. jdwp_get_threads shows transfer-A-to-B and transfer-B-to-A in MONITOR state; jdwp_dump_locks prints the lock graph with both monitor holders and the inverse-order cycle; jdwp_get_stack on each thread shows the lock-ordering inversion. (Trying jdwp_evaluate_expression / jdwp_to_string on these threads is refused — invoking a method on a monitor-blocked thread would hang the debugger, which is itself the tell.)
#10 The Oversold House
Difficulty: Hard | Test: BoxOfficeTest | Package: boxoffice | Par: 4 | Exercises: in-target probes (fast logpoint / fast breakpoint, with the user's consent)
Symptom: [Tickets sold for 300 performances of 200000 seats] expected: 60000000L but was: 60001634L — the season ends oversold by a few tickets per performance.
Solved when: you name one specific double sale: the seat, the window that sold it first and the window that sold it again. Knowing that the house is oversold, or by how much, is not enough.
BoxOffice.trySell checks that the seat is free, prices it (reading the pricing desk's volatile surcharge), and only then records the buyer: a check-then-act race. Two windows that check the same free seat within the pricing step both sell it, and sold counts both. About six seats per performance are sold twice. Customer numbers are window numbers, and owners[] keeps only the last buyer, so counting seats against sold() after the fact shows the overselling but cannot name the window that sold a seat first. Only catching the collision can.
Debug path: On the sell line (BoxOffice.java:54, seatOwners[seat] = customer), the condition seatOwners[seat] != 0 is true when another window sold the seat after this one checked it. Both in-target tools rewrite the class's bytecode, so each needs the user's explicit consent: the first call, without userApproved, arms nothing and returns a risk briefing to relay word for word. BoxOffice is not loaded when the test JVM starts; the approved probe waits for the class to load and arms on its next hit.
- Fast logpoint, 4 calls (par):
jdwp_set_fast_logpointonone.edee.jdwp.sandbox.boxoffice.BoxOfficeline 54, with that condition and the expressionseat + " sold to " + seatOwners[seat] + ", resold to " + customer, withoutuserApproved: the risk briefing, relayed to the user.- The same call with
userApproved=true, once the user agrees. jdwp_resume_until_event: the test runs to its end. The reply is[VM_DEATH]and says how manyFAST_LOGPOINThits were recorded.jdwp_get_events: the double sales, each with its seat and the first and second window.
- Fast breakpoint, 5 calls: the briefing and the approved
jdwp_set_fast_breakpointcall with the same condition, thenjdwp_resume_until_event: the first double sale stops with every window parked.jdwp_get_breakpoint_contextshowsseatand its second buyer,customer.jdwp_evaluate_expression("seatOwners[seat]")gives the first buyer; array elements are not in the context dump.
The logpoint does not catch every double sale. Its condition reads a plain int[] without synchronization, so a window that reads the seat before the other window's write is visible logs nothing; one live run caught 1,460 of 2,472 double sales. One is enough to solve the flight. The probe also sits inside the race window, between the check and the write, so it widens the race rather than hiding it: runs with the probe sold more seats twice, not fewer.
An ordinary conditional breakpoint or logpoint on that line goes through the debugger on each of about 60 million passes, at 3–30 ms each. The box office closes first, and the serialized windows stop colliding.
jdwp-java MCP soruları
jdwp-java MCP hangi araçları sunar?
50 araç sunar; bunlar arasında jdwp_connect, jdwp_disconnect, jdwp_reconnect, jdwp_wait_for_attach, jdwp_get_version yer alır.
Yapay zeka ajanı bu sunucu ile neler yapabilir?
Ajan; yerel değişkenler ve alanlar gibi çalışma zamanı durumlarını inceleyebilir, askıya alınmış çerçevelerde (frames) rastgele Java kodlarını çalıştırabilir, çalışan programları değiştirebilir ve koşullu veya istisna tabanlı breakpoint'ler ayarlayabilir.
Sunucuyu çalıştırmak için sistem gereksinimleri nelerdir?
Sunucuyu çalıştırmak için PATH üzerinde kurulu bir JDK 17+ gereklidir; kaynak koddan derlemek için ise JDK 21+ gereklidir.
Java uygulamama nasıl bağlarım?
Java uygulamanızı JDWP etkinleştirilmiş şekilde başlatmalısınız; örneğin -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:5005 argümanını kullanabilirsiniz.