ci: add Gitea CI workflows and release pipeline
Test / vet (push) Successful in 44s
Release / build (amd64, linux) (push) Successful in 39s
Release / build (arm64, linux) (push) Successful in 38s
Release / build (loong64, linux) (push) Successful in 39s
Release / build (riscv64, linux) (push) Successful in 39s
Test / test (push) Successful in 1m58s
Release / release (push) Successful in 18s
Test / build (push) Successful in 37s

Assisted-by: DeepSeek V4 Pro
This commit is contained in:
2026-08-03 19:42:05 +02:00
parent f41a86b660
commit 2ab6b9eb84
10 changed files with 573 additions and 82 deletions
+16 -17
View File
@@ -1,8 +1,6 @@
// Copyright (c) 2026 Petr Balvín <opensource@petrbalvin.org> (https://petrbalvin.org)
// SPDX-License-Identifier: BSD-3-Clause
//go:build linux && amd64
package debug
import "fmt"
@@ -85,14 +83,15 @@ func (c *Condition) Eval(regs *Regs) bool {
}
// Breakpoints manages the set of breakpoints for a Session.
// Breakpoints manages software breakpoints for a debuggee.
type Breakpoints struct {
s *Session
t tracer
bps map[uint64]*Breakpoint
}
// NewBreakpoints creates a breakpoint manager for the session.
func NewBreakpoints(s *Session) *Breakpoints {
return &Breakpoints{s: s, bps: make(map[uint64]*Breakpoint)}
// NewBreakpoints creates a new breakpoint manager.
func NewBreakpoints(t tracer) *Breakpoints {
return &Breakpoints{t: t, bps: make(map[uint64]*Breakpoint)}
}
// Set installs a breakpoint at addr (replaces any existing one).
@@ -108,14 +107,14 @@ func (bm *Breakpoints) SetWithCond(addr uint64, label string, cond *Condition) (
return bp, nil
}
// Read the original byte.
word, err := bm.s.Peek(addr)
word, err := bm.t.Peek(addr)
if err != nil {
return nil, err
}
orig := byte(word)
// Patch with INT3 (0xCC), preserving the rest of the word.
patched := (word &^ 0xFF) | 0xCC
if err := bm.s.Poke(addr, patched); err != nil {
if err := bm.t.Poke(addr, patched); err != nil {
return nil, err
}
bp := &Breakpoint{Addr: addr, Label: label, Orig: orig, Enabled: true, Cond: cond}
@@ -160,12 +159,12 @@ func (bm *Breakpoints) Clear(addr uint64) error {
if !ok {
return fmt.Errorf("debug: no breakpoint at %#x", addr)
}
word, err := bm.s.Peek(addr)
word, err := bm.t.Peek(addr)
if err != nil {
return err
}
restored := (word &^ 0xFF) | uint64(bp.Orig)
if err := bm.s.Poke(addr, restored); err != nil {
if err := bm.t.Poke(addr, restored); err != nil {
return err
}
delete(bm.bps, addr)
@@ -211,24 +210,24 @@ func (bm *Breakpoints) HandleTrap(regs *Regs) *Breakpoint {
if bp.Cond != nil && !bp.Cond.Eval(regs) {
// Condition not met — restore the byte but do NOT rewind RIP.
// The process continues from the next instruction (past the INT3).
word, err := bm.s.Peek(trapAddr)
word, err := bm.t.Peek(trapAddr)
if err == nil {
restored := (word &^ 0xFF) | uint64(bp.Orig)
bm.s.Poke(trapAddr, restored)
bm.t.Poke(trapAddr, restored)
}
// RIP is already past the INT3 (trapAddr + 1). Don't rewind.
return nil
}
bp.hits++
// Restore the original byte.
word, err := bm.s.Peek(trapAddr)
word, err := bm.t.Peek(trapAddr)
if err == nil {
restored := (word &^ 0xFF) | uint64(bp.Orig)
bm.s.Poke(trapAddr, restored)
bm.t.Poke(trapAddr, restored)
}
// Rewind RIP to re-execute the original instruction.
regs.RIP = trapAddr
bm.s.SetRegs(regs)
bm.t.SetRegs(regs)
return bp
}
@@ -240,10 +239,10 @@ func (bm *Breakpoints) Reinsert(addr uint64) error {
if !ok || !bp.Enabled {
return nil
}
word, err := bm.s.Peek(addr)
word, err := bm.t.Peek(addr)
if err != nil {
return err
}
patched := (word &^ 0xFF) | 0xCC
return bm.s.Poke(addr, patched)
return bm.t.Poke(addr, patched)
}