mirror of
https://github.com/Droid-Deck/DroidDeck.git
synced 2026-11-13 03:51:10 +00:00
feat: compress idle game memory before low-RAM devices run out (#2187)
This commit is contained in:
1 parent
f33d5b9e09
commit
066834d17d
4 files changed
+336
-6
No files matched your search
@@ -41,6 +41,63 @@ reduced aggregate launcher RSS only from 263 to 255 MiB and backed off. Thus lau
|
||||
does not resolve this load failure, and this run did not exhaust swap. The unevictable/shmem
|
||||
growth needs allocation attribution before treating it as a driver or GPU-memory diagnosis.
|
||||
|
||||
## Low-RAM devices: compressing idle game memory
|
||||
|
||||
On devices with at most 13 GiB `MemTotal` (every 12 GB phone and handheld and below), the
|
||||
monitor also compresses the game session itself when memory runs out. The trigger is absolute
|
||||
headroom, not a percentage: Android's `lmkd` killed a session on a 7.5 GiB device with
|
||||
490 MiB still available, so a 95% used threshold (about 400 MiB left on 8 GB) is already past the
|
||||
kill. A world load added 1 GiB of GPU memory in about ten seconds, and compressing 1.4 GiB took
|
||||
2.6 seconds, so the request has to leave room for both.
|
||||
|
||||
The session request runs when available memory falls below a quarter of RAM (bounded to
|
||||
768–2560 MiB, 1.85 GiB on an 8 GB device), or below one and a half times that with at least 1%
|
||||
`some avg10` memory stall. One request pages out every session process that loaded
|
||||
`libblsession.so`: the game, Wine and its services, plus the launcher processes unless their
|
||||
compression is turned off, under a single wait. It runs before the launcher-only check, which
|
||||
can wait up to 12 seconds for acknowledgements. While a game runs and available memory is under
|
||||
twice that level, memory is read every second instead of every five. Requests are at least 30 seconds apart and need the same swap reserve as launcher
|
||||
requests. When the session has read back more than half of what the previous request compressed,
|
||||
that memory is in use: the request is skipped and the wait doubles, up to five minutes. A
|
||||
request whose memory stayed compressed resets it to 30 seconds. Below half the headroom the
|
||||
wait is ignored and only the 30-second spacing applies. The state resets when the game exits. Devices above 13 GiB keep the
|
||||
launcher-only behaviour.
|
||||
|
||||
Measured on a Retroid Pocket Duo (7.5 GiB, kernel 6.6, 6 GiB lzo-rle zram plus a 4 GiB swap file)
|
||||
entering the world in World of Warcraft: Forever, one run each:
|
||||
|
||||
| | No session page-out | Session compressed at character select |
|
||||
| --- | --- | --- |
|
||||
| available before Enter World | 1,569–1,800 MiB | 2,329 MiB |
|
||||
| game resident / in zram before Enter World | 1,919 / 13 MiB | 409 / 1,523 MiB |
|
||||
| GPU (KGSL) memory during the load | 0.97 → 1.99 GiB | 0.96 → 1.99 GiB |
|
||||
| lowest available | 491 MiB | 623 MiB |
|
||||
| result | `LOW_MEMORY` kill of the whole session 58 s after Enter World | in the world at 35 FPS after three minutes |
|
||||
|
||||
The killed run had 7.4 GiB of swap free at the time: reclaim did not keep up with the GPU
|
||||
allocation, not swap capacity. Little Nightmares at the same settings compressed from 1.41 GiB to
|
||||
0.25 GiB resident in 2.6 seconds (1.1 GiB stored in about 0.5 GiB of zram) and read back 70 MiB in
|
||||
the following minute at its menu and 440 MiB while loading the next area; frame rate stayed at
|
||||
44–46 FPS with one or two short frame-time spikes. These are single runs, not averages.
|
||||
|
||||
On the PR build without any manual step, the monitor compressed WoW before character select and
|
||||
again during the load; the game entered the world and stayed there at 33 FPS. Once in the world,
|
||||
available memory settled near 1 GiB, under the headroom, and the first version of the policy
|
||||
compressed 200–400 MiB every 30 seconds while the game read back about 15 MiB/s. The
|
||||
read-back check therefore compares against the last request instead of all compressed memory.
|
||||
|
||||
A second run on that build showed the trigger arriving too late: character select sat at
|
||||
1.5–1.6 GiB available, just above the original one-fifth headroom, so nothing was compressed
|
||||
there. During the load, available memory crossed the headroom 9 seconds after Enter World, but the
|
||||
launcher check ran first and waited 12 seconds for acknowledgements; the game request started at
|
||||
21 seconds, moved about 200 MiB, and `lmkd` ended the session at 30 seconds. The quarter-of-RAM
|
||||
level and the game-first single request address both.
|
||||
|
||||
Without root an app cannot write its memory cgroup's `memory.reclaim` (owned by `system`),
|
||||
`process_madvise` on another process returns `EPERM`, `PAGEMAP_SCAN` is absent before Linux 6.7,
|
||||
and KGSL's per-process GPU reclaim `state` is root-only. Page-out therefore runs inside each
|
||||
process through the preload, and GPU memory itself cannot be compressed from userspace.
|
||||
|
||||
## CRAM and the bounded pager experiment
|
||||
|
||||
[Meta's CRAM proposal](https://lpc.events/event/20/contributions/2424/) uses memory compression
|
||||
|
||||
@@ -840,6 +840,9 @@ case $mode in
|
||||
_bl_headroom=$((_bl_mem_total / 5))
|
||||
[ "$_bl_headroom" -ge $((512 * 1024)) ] || _bl_headroom=$((512 * 1024))
|
||||
[ "$_bl_headroom" -le $((1536 * 1024)) ] || _bl_headroom=$((1536 * 1024))
|
||||
_bl_tier_headroom=$((_bl_mem_total / 4))
|
||||
[ "$_bl_tier_headroom" -ge $((768 * 1024)) ] || _bl_tier_headroom=$((768 * 1024))
|
||||
[ "$_bl_tier_headroom" -le $((2560 * 1024)) ] || _bl_tier_headroom=$((2560 * 1024))
|
||||
_bl_swap_reserve=$((_bl_swap_total / 5))
|
||||
[ "$_bl_swap_reserve" -ge $((256 * 1024)) ] || _bl_swap_reserve=$((256 * 1024))
|
||||
}
|
||||
@@ -884,6 +887,88 @@ case $mode in
|
||||
_bl_pressure_backoff=30
|
||||
fi
|
||||
}
|
||||
bl_memory_critical() {
|
||||
[ "$_bl_mem_total" -gt 0 ] && [ "$_bl_available" -ge 0 ] &&
|
||||
{ [ "$_bl_available" -lt "$_bl_tier_headroom" ] ||
|
||||
{ [ "$_bl_psi" -ge 1 ] && [ "$_bl_available" -lt $((_bl_tier_headroom * 3 / 2)) ]; }; }
|
||||
}
|
||||
bl_low_ram() {
|
||||
[ "$_bl_mem_total" -gt 0 ] && [ "$_bl_mem_total" -le $((13 * 1024 * 1024)) ]
|
||||
}
|
||||
bl_session_pressure() {
|
||||
bl_memory_read
|
||||
[ "$_bl_game" = 1 ] && bl_low_ram && bl_memory_critical || return 0
|
||||
local _wait=$_bl_tier_backoff _urgent=0 _before _pids _targets
|
||||
[ "$_bl_available" -ge $((_bl_headroom / 2)) ] || _urgent=1 _wait=30
|
||||
[ "$_bl_tier_at" -lt 0 ] || [ $((SECONDS - _bl_tier_at)) -ge "$_wait" ] || return 0
|
||||
if [ "$_bl_tier_at" -ge 0 ] && [ "$_bl_tier_pushed" -gt 0 ]; then
|
||||
bl_tier_readback
|
||||
if [ "$_bl_tier_back" -le $((_bl_tier_pushed / 2)) ]; then
|
||||
_bl_tier_backoff=30
|
||||
elif [ "$_urgent" = 0 ]; then
|
||||
_bl_tier_backoff=$((_bl_tier_backoff * 2 > 300 ? 300 : _bl_tier_backoff * 2))
|
||||
_bl_tier_at=$SECONDS _bl_tier_pushed=0
|
||||
step "zRAM: the game read back most of its last page-out, next try in ${_bl_tier_backoff} s"
|
||||
return 0
|
||||
fi
|
||||
fi
|
||||
_bl_tier_at=$SECONDS
|
||||
bl_zram_pick game
|
||||
bl_zram_usage "${_pids[@]}" > /dev/null
|
||||
[ "$_bl_rss" -ge $((256 * 1024)) ] || return 0
|
||||
if [ "$_bl_swap_total" -le 0 ] || [ $((_bl_swap_free - _bl_rss)) -lt "$_bl_swap_reserve" ]; then
|
||||
step "zRAM: low-memory page-out skipped, swap headroom too small ($((_bl_swap_free / 1024)) MB free)"
|
||||
return 0
|
||||
fi
|
||||
_targets=("${_pids[@]}") _before=$_bl_swap
|
||||
bl_zram_send 1 "low memory (available $((_bl_available / 1024)) MB), compressing idle game memory" game
|
||||
bl_zram_usage "${_targets[@]}" > /dev/null
|
||||
_bl_tier_pushed=$((_bl_swap - _before))
|
||||
bl_tier_note "${_targets[@]}"
|
||||
if [ "${_bl_pageout_complete:-0}" = 1 ] && [ "${BL_ZRAM_CLIENT:-1}" = 1 ] && [ "${#_bl_pids[@]}" -gt 0 ]; then
|
||||
_bl_pressure_at=$SECONDS _bl_packed=$SECONDS
|
||||
fi
|
||||
}
|
||||
bl_proc_start() {
|
||||
local _s=
|
||||
_bl_start=
|
||||
read -r _s 2>/dev/null < "/proc/$1/stat" || return 1
|
||||
_s=${_s##*) }
|
||||
set -- $_s
|
||||
[ "$1" != Z ] && [ "$#" -ge 20 ] || return 1
|
||||
_bl_start=${20}
|
||||
}
|
||||
bl_tier_note() {
|
||||
local _p
|
||||
_bl_tier_targets=() _bl_tier_swaps=() _bl_tier_starts=()
|
||||
for _p in "$@"; do
|
||||
bl_proc_start "$_p" || continue
|
||||
bl_zram_usage "$_p" > /dev/null
|
||||
_bl_tier_targets+=("$_p") _bl_tier_swaps+=("$_bl_swap") _bl_tier_starts+=("$_bl_start")
|
||||
done
|
||||
}
|
||||
bl_tier_readback() {
|
||||
local _i _back=0
|
||||
for _i in "${!_bl_tier_targets[@]}"; do
|
||||
bl_proc_start "${_bl_tier_targets[$_i]}" && [ "$_bl_start" = "${_bl_tier_starts[$_i]}" ] || continue
|
||||
bl_zram_usage "${_bl_tier_targets[$_i]}" > /dev/null
|
||||
[ "${_bl_tier_swaps[$_i]}" -le "$_bl_swap" ] || _back=$((_back + _bl_tier_swaps[_i] - _bl_swap))
|
||||
done
|
||||
_bl_tier_back=$_back
|
||||
}
|
||||
bl_watch_nap() {
|
||||
local _i
|
||||
if [ "$_bl_game" = 1 ] && bl_low_ram && [ "$_bl_available" -ge 0 ] && [ "$_bl_available" -lt $((_bl_tier_headroom * 2)) ]; then
|
||||
for _i in 1 2 3 4 5; do
|
||||
nap 1
|
||||
bl_memory_read
|
||||
bl_memory_critical && return 0
|
||||
done
|
||||
else
|
||||
nap 5
|
||||
fi
|
||||
return 0
|
||||
}
|
||||
bl_session_others() {
|
||||
local _p
|
||||
_bl_session=()
|
||||
@@ -892,7 +977,11 @@ case $mode in
|
||||
done
|
||||
}
|
||||
bl_zram_pick() {
|
||||
if [ "$1" = session ]; then
|
||||
if [ "$1" = game ]; then
|
||||
bl_session_others
|
||||
_pids=("${_bl_session[@]}")
|
||||
[ "${BL_ZRAM_CLIENT:-1}" != 1 ] || _pids+=("${_bl_pids[@]}")
|
||||
elif [ "$1" = session ]; then
|
||||
bl_session_others
|
||||
_pids=("${_bl_session[@]}")
|
||||
else
|
||||
@@ -954,7 +1043,7 @@ case $mode in
|
||||
[ "$_bl_ram_cap" -gt 0 ] && { [ "$_bl_measured" -lt 0 ] || [ $((SECONDS - _bl_measured)) -ge 30 ]; }
|
||||
}
|
||||
_bl_since=-1 _bl_since_apps= _bl_packed=-1 _bl_measured=-1 _bl_unpack=0 _bl_capped=-1 _bl_backoff=30
|
||||
_bl_pressure_at=-1 _bl_pressure_backoff=30
|
||||
_bl_pressure_at=-1 _bl_pressure_backoff=30 _bl_tier_at=-1 _bl_tier_backoff=30 _bl_tier_pushed=0 _bl_tier_back=0 _bl_tier_targets=() _bl_tier_swaps=() _bl_tier_starts=()
|
||||
while kill -0 $$ 2>/dev/null; do
|
||||
bl_zram_scan
|
||||
bl_memory_read
|
||||
@@ -981,6 +1070,7 @@ case $mode in
|
||||
_bl_backoff=30
|
||||
fi
|
||||
fi
|
||||
bl_session_pressure
|
||||
bl_client_pressure
|
||||
if [ "$_bl_game" = 1 ] && bl_game_focused; then
|
||||
if [ "$_bl_since" -lt 0 ] || [ "$_bl_since_apps" != "$_bl_apps" ]; then
|
||||
@@ -995,7 +1085,7 @@ case $mode in
|
||||
if [ "$_bl_packed" -ge 0 ]; then
|
||||
_bl_unpack=1
|
||||
fi
|
||||
_bl_since=-1 _bl_packed=-1
|
||||
_bl_since=-1 _bl_packed=-1 _bl_tier_at=-1 _bl_tier_backoff=30 _bl_tier_pushed=0 _bl_tier_targets=() _bl_tier_swaps=() _bl_tier_starts=()
|
||||
else
|
||||
_bl_since=-1
|
||||
fi
|
||||
@@ -1003,7 +1093,7 @@ case $mode in
|
||||
bl_zram_send 0 "memory recovered, reading the client back" client
|
||||
_bl_unpack=0
|
||||
fi
|
||||
nap 5
|
||||
bl_watch_nap
|
||||
done
|
||||
) &
|
||||
fi
|
||||
|
||||
@@ -81,7 +81,7 @@ class ClientZramFocusTest(unittest.TestCase):
|
||||
def test_refocus_restarts_the_delay_and_game_exit_restores_a_packed_client(self):
|
||||
source = SCRIPT.read_text()
|
||||
start = source.index(' if [ "$_bl_game" = 1 ] && bl_game_focused; then')
|
||||
stop = source.index(' nap 5', start)
|
||||
stop = source.index(' bl_watch_nap\n', start)
|
||||
body = source[start:stop]
|
||||
harness = '''
|
||||
_bl_since=-1 _bl_since_apps= _bl_packed=-1 _bl_unpack=0 _bl_apps=" 413150"
|
||||
@@ -112,7 +112,7 @@ tick 60 0 0
|
||||
def monitor_harness(self):
|
||||
source = SCRIPT.read_text()
|
||||
start = source.index(' bl_zram_scan\n bl_memory_read\n')
|
||||
stop = source.index(' nap 5', start)
|
||||
stop = source.index(' bl_watch_nap\n', start)
|
||||
return '''
|
||||
_bl_since=-1 _bl_since_apps= _bl_packed=-1 _bl_measured=-1 _bl_unpack=0 _bl_capped=-1 _bl_backoff=30
|
||||
_bl_apps=" 413150"
|
||||
@@ -121,6 +121,7 @@ step() { :; }
|
||||
bl_zram_scan() { :; }
|
||||
bl_memory_read() { :; }
|
||||
bl_client_pressure() { :; }
|
||||
bl_session_pressure() { :; }
|
||||
bl_client_can_pageout() { return 0; }
|
||||
bl_client_can_readback() { [ "${readback:-1}" = 1 ]; }
|
||||
bl_game_focused() { [ "$focused" = 1 ]; }
|
||||
|
||||
@@ -137,5 +137,187 @@ echo "backoff:$_bl_pressure_backoff"
|
||||
self.assertEqual(['no'], self.run_policy('if bl_client_can_readback; then echo yes; else echo no; fi'))
|
||||
|
||||
|
||||
class SessionPressureTest(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.directory = tempfile.TemporaryDirectory()
|
||||
self.addCleanup(self.directory.cleanup)
|
||||
self.root = Path(self.directory.name)
|
||||
self.memory()
|
||||
|
||||
def memory(self, available=1024, swap_free=6144, total=7168, swap_total=8192, psi='0.00'):
|
||||
(self.root / 'meminfo').write_text(
|
||||
f'MemTotal: {total * 1024} kB\nMemAvailable: {available * 1024} kB\n'
|
||||
f'SwapTotal: {swap_total * 1024} kB\nSwapFree: {swap_free * 1024} kB\n')
|
||||
(self.root / 'pressure').write_text(f'some avg10={psi} avg60=0.00 avg300=0.00 total=1234\n')
|
||||
|
||||
def run_policy(self, body):
|
||||
functions = '\n'.join(function_source(name) for name in (
|
||||
'bl_memory_read', 'bl_memory_tight', 'bl_memory_critical', 'bl_low_ram',
|
||||
'bl_session_pressure', 'bl_watch_nap', 'bl_zram_pick', 'bl_tier_note', 'bl_tier_readback'))
|
||||
functions = functions.replace('/proc/meminfo', str(self.root / 'meminfo'))
|
||||
functions = functions.replace('/proc/pressure/memory', str(self.root / 'pressure'))
|
||||
harness = '''
|
||||
set -u
|
||||
_bl_game=1 _bl_tier_at=-1 _bl_tier_backoff=30 _bl_tier_pushed=0 _bl_tier_back=0 _bl_tier_targets=() _bl_tier_swaps=() _bl_tier_starts=() _bl_pids=(999)
|
||||
_bl_pressure_at=-1 _bl_packed=-1
|
||||
rss=$((1536 * 1024)) swap=0 pushed=$((1024 * 1024)) alive=999 started=100 complete=1
|
||||
step() { :; }
|
||||
bl_proc_start() { _bl_start=; case " $alive " in *" $1 "*) _bl_start=$started; return 0 ;; esac; return 1; }
|
||||
nap() { echo "nap:$1"; }
|
||||
bl_session_others() { _bl_session=(321 654); }
|
||||
bl_zram_usage() { _bl_rss=$rss _bl_swap=$swap; }
|
||||
bl_zram_send() { echo "send:$1:$3"; rss=$((rss - pushed)); swap=$((swap + pushed)); _bl_pageout_complete=$complete; }
|
||||
''' + functions + '\nbl_memory_read\n' + body
|
||||
result = run_bash(harness)
|
||||
self.assertEqual(0, result.returncode, result.stderr)
|
||||
self.assertEqual('', result.stderr)
|
||||
return result.stdout.splitlines()
|
||||
|
||||
def test_low_headroom_compresses_the_game_session_at_most_every_30_seconds(self):
|
||||
self.assertEqual(['send:1:game', 'send:1:game'], self.run_policy('''
|
||||
SECONDS=0; bl_session_pressure
|
||||
SECONDS=10; bl_session_pressure
|
||||
SECONDS=29; bl_session_pressure
|
||||
SECONDS=30; bl_session_pressure
|
||||
'''))
|
||||
|
||||
def test_twelve_gigabyte_devices_are_included_and_larger_ones_are_not(self):
|
||||
for total, expected in ((11264, ['send:1:game']), (12288, ['send:1:game']), (15360, [])):
|
||||
with self.subTest(total=total):
|
||||
self.memory(total=total)
|
||||
self.assertEqual(expected, self.run_policy('bl_session_pressure'))
|
||||
|
||||
def test_no_game_does_not_compress_the_session(self):
|
||||
self.assertEqual([], self.run_policy('_bl_game=0; bl_session_pressure'))
|
||||
|
||||
def test_enough_available_memory_does_not_compress_even_with_stalls(self):
|
||||
self.memory(available=3072, psi='5.00')
|
||||
self.assertEqual([], self.run_policy('bl_session_pressure'))
|
||||
|
||||
def test_stalls_with_moderate_headroom_compress_before_the_hard_limit(self):
|
||||
self.memory(available=2048, psi='1.50')
|
||||
self.assertEqual(['send:1:game'], self.run_policy('bl_session_pressure'))
|
||||
|
||||
def test_moderate_headroom_without_stalls_waits(self):
|
||||
self.memory(available=2048)
|
||||
self.assertEqual([], self.run_policy('bl_session_pressure'))
|
||||
|
||||
def test_game_reading_back_most_of_a_pageout_skips_and_doubles_the_wait(self):
|
||||
self.assertEqual(['send:1:game', 'backoff:60', 'send:1:game', 'backoff:60',
|
||||
'send:1:game', 'backoff:30'], self.run_policy('''
|
||||
pushed=$((512 * 1024))
|
||||
SECONDS=0; bl_session_pressure
|
||||
swap=$((swap / 4))
|
||||
SECONDS=30; bl_session_pressure
|
||||
echo "backoff:$_bl_tier_backoff"
|
||||
SECONDS=60; bl_session_pressure
|
||||
SECONDS=90; bl_session_pressure
|
||||
echo "backoff:$_bl_tier_backoff"
|
||||
SECONDS=150; bl_session_pressure
|
||||
echo "backoff:$_bl_tier_backoff"
|
||||
'''))
|
||||
|
||||
def test_repeated_read_back_keeps_doubling_up_to_five_minutes(self):
|
||||
self.assertEqual(['backoff:300'], self.run_policy('''
|
||||
_bl_tier_backoff=240 _bl_tier_at=0 _bl_tier_targets=(999) _bl_tier_swaps=($((1024 * 1024))) _bl_tier_starts=(100) _bl_tier_pushed=$((1024 * 1024)) swap=0
|
||||
SECONDS=240; bl_session_pressure
|
||||
echo "backoff:$_bl_tier_backoff"
|
||||
'''))
|
||||
|
||||
def test_nearly_out_of_memory_ignores_the_backoff_but_keeps_30_seconds(self):
|
||||
self.memory(available=512)
|
||||
self.assertEqual(['send:1:game', 'send:1:game'], self.run_policy('''
|
||||
_bl_tier_backoff=300
|
||||
SECONDS=0; bl_session_pressure
|
||||
swap=0
|
||||
SECONDS=20; bl_session_pressure
|
||||
SECONDS=30; bl_session_pressure
|
||||
'''))
|
||||
|
||||
def test_small_swap_headroom_skips_the_pageout(self):
|
||||
self.memory(swap_free=1536, swap_total=2048)
|
||||
self.assertEqual([], self.run_policy('bl_session_pressure'))
|
||||
|
||||
def test_small_session_is_left_alone(self):
|
||||
self.assertEqual([], self.run_policy('rss=$((128 * 1024)); bl_session_pressure'))
|
||||
|
||||
def test_watch_polls_every_second_near_the_limit_and_stops_when_critical(self):
|
||||
self.memory(available=2560)
|
||||
self.assertEqual(['nap:1', 'nap:1', 'nap:1', 'nap:1', 'nap:1'], self.run_policy('bl_watch_nap'))
|
||||
self.memory(available=1024)
|
||||
self.assertEqual(['nap:1'], self.run_policy('_bl_available=$((2560 * 1024)); bl_watch_nap'))
|
||||
self.memory(available=4096)
|
||||
self.assertEqual(['nap:5'], self.run_policy('bl_watch_nap'))
|
||||
self.assertEqual(['nap:5'], self.run_policy('_bl_game=0; bl_watch_nap'))
|
||||
|
||||
def test_one_push_compresses_the_game_and_the_launcher_and_restarts_the_launcher_wait(self):
|
||||
self.assertEqual(['send:1:game', 'pids:321 654 999', 'client:0:0'], self.run_policy('''
|
||||
SECONDS=0; bl_session_pressure
|
||||
bl_zram_pick game; echo "pids:${_pids[*]}"
|
||||
echo "client:$_bl_pressure_at:$_bl_packed"
|
||||
'''))
|
||||
|
||||
def test_launcher_compression_turned_off_keeps_the_launcher_out(self):
|
||||
self.assertEqual(['pids:321 654', 'client:-1:-1'], self.run_policy('''
|
||||
BL_ZRAM_CLIENT=0
|
||||
SECONDS=0; bl_session_pressure
|
||||
bl_zram_pick game; echo "pids:${_pids[*]}"
|
||||
echo "client:$_bl_pressure_at:$_bl_packed"
|
||||
''')[1:])
|
||||
|
||||
def test_low_ram_devices_compress_with_a_quarter_of_memory_left(self):
|
||||
self.memory(available=1700)
|
||||
self.assertEqual(['send:1:game'], self.run_policy('bl_session_pressure'))
|
||||
self.memory(available=1900)
|
||||
self.assertEqual([], self.run_policy('bl_session_pressure'))
|
||||
|
||||
|
||||
def test_a_compressed_process_that_exits_is_not_counted_as_read_back(self):
|
||||
self.assertEqual(['send:1:game', 'send:1:game', 'backoff:30'], self.run_policy('''
|
||||
SECONDS=0; bl_session_pressure
|
||||
alive=""; swap=0
|
||||
SECONDS=30; bl_session_pressure
|
||||
echo "backoff:$_bl_tier_backoff"
|
||||
'''))
|
||||
|
||||
def test_a_process_that_starts_after_a_pageout_is_not_in_its_read_back(self):
|
||||
self.assertEqual(['send:1:game', 'targets:999', 'send:1:game', 'backoff:30'], self.run_policy('''
|
||||
SECONDS=0; bl_session_pressure
|
||||
echo "targets:${_bl_tier_targets[*]}"
|
||||
alive="999 1234"
|
||||
SECONDS=30; bl_session_pressure
|
||||
echo "backoff:$_bl_tier_backoff"
|
||||
'''))
|
||||
|
||||
def test_an_incomplete_pageout_does_not_restart_the_launcher_wait(self):
|
||||
self.assertEqual(['send:1:game', 'client:-1:-1'], self.run_policy('''
|
||||
complete=0
|
||||
SECONDS=0; bl_session_pressure
|
||||
echo "client:$_bl_pressure_at:$_bl_packed"
|
||||
'''))
|
||||
|
||||
|
||||
def test_a_reused_process_id_is_not_counted_as_read_back(self):
|
||||
self.assertEqual(['send:1:game', 'send:1:game', 'backoff:30'], self.run_policy('''
|
||||
SECONDS=0; bl_session_pressure
|
||||
started=200; swap=0
|
||||
SECONDS=30; bl_session_pressure
|
||||
echo "backoff:$_bl_tier_backoff"
|
||||
'''))
|
||||
|
||||
def test_process_start_comes_from_stat_and_zombies_have_none(self):
|
||||
stat = self.root / 'stat'
|
||||
source = function_source('bl_proc_start').replace('/proc/$1/stat', str(stat))
|
||||
fields = ' '.join(['0'] * 17)
|
||||
for state, expected in (('S', ['ok:4242']), ('Z', ['gone:'])):
|
||||
with self.subTest(state=state):
|
||||
stat.write_text(f'77 (Wow (arm64).exe) {state} 1 {fields} 4242 99 0\n')
|
||||
result = run_bash(source + '\nif bl_proc_start 77; then echo "ok:$_bl_start"; else echo "gone:$_bl_start"; fi')
|
||||
self.assertEqual(expected, result.stdout.split())
|
||||
stat.unlink()
|
||||
result = run_bash(source + '\nbl_proc_start 77 || echo gone')
|
||||
self.assertEqual(['gone'], result.stdout.split())
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
Reference in new issue
Block a user