Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 25 additions & 6 deletions internal/memory/modify/freeze.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import (
"memodroid/internal/memory/search"
)

const freezeInterval = 100 * time.Millisecond
const defaultFreezeInterval = 100 * time.Millisecond

type freezeEntry struct {
drv driver.Driver
Expand All @@ -21,15 +21,33 @@ type freezeEntry struct {

// Freezer manages a set of frozen addresses.
type Freezer struct {
mu sync.Mutex
entries map[uintptr]*freezeEntry
mu sync.Mutex
entries map[uintptr]*freezeEntry
interval time.Duration
}

func NewFreezer() *Freezer {
return &Freezer{entries: make(map[uintptr]*freezeEntry)}
return &Freezer{
entries: make(map[uintptr]*freezeEntry),
interval: defaultFreezeInterval,
}
}

// SetInterval changes the freeze write interval for newly frozen addresses.
func (f *Freezer) SetInterval(d time.Duration) {
f.mu.Lock()
defer f.mu.Unlock()
f.interval = d
}

// GetInterval returns the current freeze interval.
func (f *Freezer) GetInterval() time.Duration {
f.mu.Lock()
defer f.mu.Unlock()
return f.interval
}

// Freeze starts a goroutine that repeatedly writes value to addr every 100ms.
// Freeze starts a goroutine that repeatedly writes value to addr at the configured interval.
// Returns an error if addr is already frozen.
func (f *Freezer) Freeze(drv driver.Driver, pid int, addr uintptr, value []byte) error {
f.mu.Lock()
Expand All @@ -42,11 +60,12 @@ func (f *Freezer) Freeze(drv driver.Driver, pid int, addr uintptr, value []byte)
val := make([]byte, len(value))
copy(val, value)

iv := f.interval
e := &freezeEntry{drv: drv, pid: pid, addr: addr, value: val, stop: make(chan struct{})}
f.entries[addr] = e

go func() {
ticker := time.NewTicker(freezeInterval)
ticker := time.NewTicker(iv)
defer ticker.Stop()
for {
select {
Expand Down
17 changes: 17 additions & 0 deletions internal/server/handlers.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"fmt"
"net/http"
"strconv"
"time"

"memodroid/internal/app"
"memodroid/internal/driver/adb"
Expand Down Expand Up @@ -522,6 +523,22 @@ func (h *handler) memoryFreeze(w http.ResponseWriter, r *http.Request) {
writeJSON(w, map[string]any{"ok": true})
}

func (h *handler) freezeSetInterval(w http.ResponseWriter, r *http.Request) {
var req struct {
IntervalMs int `json:"interval_ms"`
}
if err := decode(r, &req); err != nil {
writeError(w, 400, err.Error())
return
}
if req.IntervalMs <= 0 {
writeError(w, 400, "interval_ms must be positive")
return
}
h.state.Freezer.SetInterval(time.Duration(req.IntervalMs) * time.Millisecond)
writeJSON(w, map[string]any{"ok": true, "interval_ms": req.IntervalMs})
}

func (h *handler) memoryFreezeAll(w http.ResponseWriter, _ *http.Request) {
sess := h.state.GetSession()
if sess == nil || !sess.HasCandidates() {
Expand Down
1 change: 1 addition & 0 deletions internal/server/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ func Start(addr string, state *app.State, d *adb.ADB) error {
mux.HandleFunc("/api/memory/modify", h.memoryModify)
mux.HandleFunc("/api/memory/undo", h.memoryUndo)
mux.HandleFunc("/api/memory/freeze", h.memoryFreeze)
mux.HandleFunc("/api/memory/freeze-interval", h.freezeSetInterval)
mux.HandleFunc("/api/memory/freeze-all", h.memoryFreezeAll)
mux.HandleFunc("/api/memory/unfreeze", h.memoryUnfreeze)
mux.HandleFunc("/api/memory/frozen", h.memoryFrozen)
Expand Down
10 changes: 10 additions & 0 deletions internal/server/static/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -391,6 +391,10 @@
<input id="unfreeze-addr" placeholder="Address to unfreeze (hex)">
<button class="btn danger" onclick="memUnfreeze()">Unfreeze</button>
</div>
<div class="row" style="margin-top:8px;border-top:1px solid var(--border);padding-top:8px">
<input id="freeze-interval" placeholder="Interval ms [100]" style="max-width:130px">
<button class="btn sm" onclick="setFreezeInterval()">Set interval</button>
</div>
</div>
</div>
<div class="card">
Expand Down Expand Up @@ -793,6 +797,12 @@
log(r.error||'Unfrozen', !r.error);
loadFrozen();
}
async function setFreezeInterval() {
const ms = parseInt(document.getElementById('freeze-interval').value);
if (!ms || ms <= 0) { log('Invalid interval', false); return; }
const r = await api('/memory/freeze-interval', {interval_ms: ms});
log(r.error || `Freeze interval: ${ms}ms`, !r.error);
}

// ── Maps ─────────────────────────────────────────────
let allMaps = [];
Expand Down
17 changes: 17 additions & 0 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -595,6 +595,22 @@ func main() {
} else {
fmt.Printf("Freezing 0x%x\n", addr)
}
case "17i":
s := prompt(fmt.Sprintf("Freeze interval [current: %v]: ", st.Freezer.GetInterval()))
if s == "" {
continue
}
d, err := time.ParseDuration(s)
if err != nil {
fmt.Println("Invalid duration (e.g. 50ms, 200ms, 1s)")
continue
}
if d <= 0 {
fmt.Println("Interval must be positive")
continue
}
st.Freezer.SetInterval(d)
fmt.Printf("Freeze interval set to %v\n", d)
case "17a":
if requireSession(sess) {
count := st.Freezer.FreezeAllCandidates(drv, sess)
Expand Down Expand Up @@ -782,6 +798,7 @@ func printMenu(st *app.State, d *adb.ADB) {
fmt.Println(" 15. Modify Address")
fmt.Println(" 16. Undo Last Modify")
fmt.Println(" 17. Freeze Address")
fmt.Println("17i. Set Freeze Interval")
fmt.Println("17a. Freeze All Candidates")
fmt.Println(" 18. Unfreeze Address")
fmt.Println(" 19. List Frozen")
Expand Down
Loading