{"record":{"id":"3488b2ffaa982b80","repo":"go-delve/delve","slug":"hardware-breakpoints-exhausted","errorCode":null,"errorMessage":"hardware breakpoints exhausted","messagePattern":"hardware breakpoints exhausted","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/proc/amd64util/debugregs.go","lineNumber":63,"sourceCode":"\tcase 0x0:\n\t\tsz = 1\n\tcase 0x1:\n\t\tsz = 2\n\tcase 0x2:\n\t\tsz = 8 // sic\n\tcase 0x3:\n\t\tsz = 4\n\t}\n\treturn addr, read, write, sz\n}\n\n// SetBreakpoint sets hardware breakpoint at index 'idx' to the specified\n// address, read/write flags and size.\n// If the breakpoint is already in use but the parameters match it does\n// nothing.\nfunc (drs *DebugRegisters) SetBreakpoint(idx uint8, addr uint64, read, write bool, sz int) error {\n\tif int(idx) >= len(drs.pAddrs) {\n\t\treturn errors.New(\"hardware breakpoints exhausted\")\n\t}\n\tcuraddr, curread, curwrite, cursz := drs.breakpoint(idx)\n\tif curaddr != 0 {\n\t\tif (curaddr != addr) || (curread != read) || (curwrite != write) || (cursz != sz) {\n\t\t\treturn fmt.Errorf(\"hardware breakpoint %d already in use (address %#x)\", idx, curaddr)\n\t\t}\n\t\t// hardware breakpoint already set\n\t\treturn nil\n\t}\n\n\tif read && !write {\n\t\treturn errors.New(\"break on read only not supported\")\n\t}\n\n\t*(drs.pAddrs[idx]) = addr\n\tvar lenrw uint64\n\tif write {\n\t\tlenrw |= 0x1","sourceCodeStart":45,"sourceCodeEnd":81,"githubUrl":"https://github.com/go-delve/delve/blob/a23773e6c31361e43246bc43a424ee009679b174/pkg/proc/amd64util/debugregs.go#L45-L81","documentation":"DebugRegisters.SetBreakpoint programs one of the x86 hardware debug registers (DR0-DR3). CPUs only provide four hardware breakpoints, so pAddrs has length 4; requesting an index >= 4 means all debug registers would be exhausted. Delve throws this instead of silently reusing or evicting an existing hardware breakpoint.","triggerScenarios":"Calling SetBreakpoint (directly or via proc's hardware breakpoint support) with idx >= len(drs.pAddrs), e.g. setting a 5th watchpoint/hardware breakpoint on a single thread while the previous four are still armed.","commonSituations":"Setting more than 4 simultaneous watchpoints in a debug session; watchpoints leaked across threads/iterations because they were never cleared; users expecting unlimited watchpoints like software breakpoints.","solutions":["Reduce the number of simultaneous hardware breakpoints to at most 4 per thread","Clear unused hardware breakpoints (ClearBreakpoint/zeroing the debug register) before setting new ones","Fall back to software breakpoints for the extra locations","Check the return error of SetBreakpoint and surface 'watchpoint limit reached' to the user instead of retrying"],"exampleFix":"// before\ndrs.SetBreakpoint(4, addr, false, true, 8) // panics into error: exhausted\n// after\nconst maxHW = 4\nif idx >= maxHW { return fmt.Errorf(\"watchpoint limit reached (max %d)\", maxHW) }\ndrs.SetBreakpoint(uint8(idx), addr, false, true, 8)","handlingStrategy":"validation","validationCode":"const maxHWBreakpoints = 4\nif idx >= maxHWBreakpoints {\n    return fmt.Errorf(\"hardware breakpoint limit is %d, cannot set index %d\", maxHWBreakpoints, idx)\n}","typeGuard":null,"tryCatchPattern":"if err := drs.SetBreakpoint(idx, addr, read, write, sz); err != nil {\n    if err.Error() == \"hardware breakpoints exhausted\" {\n        // fall back to software breakpoint\n    }\n}","preventionTips":["Track how many hardware breakpoints are armed per thread before adding one","Clear watchpoints when done — don't leave DR0-DR3 programmed","Cap UI watchpoint creation at 4 and explain the CPU limit"],"tags":["debugger","hardware-breakpoints","x86","resource-limit"],"backgroundTag":"hardware-breakpoints-exhausted","analyzedSha":"a23773e6c31361e43246bc43a424ee009679b174","analyzedAt":"2026-08-31T15:12:45.221Z","schemaVersion":2},"datasetVersion":"2026-08-31T19:17:28.585Z"}