mirror of
https://github.com/Droid-Deck/DroidDeck.git
synced 2026-11-11 02:51:10 +00:00
92 lines
4.2 KiB
Python
92 lines
4.2 KiB
Python
import json
|
|
import os
|
|
from pathlib import Path
|
|
import shutil
|
|
import subprocess
|
|
import sys
|
|
import tempfile
|
|
import unittest
|
|
|
|
TOOLS = Path(__file__).resolve().parents[1]
|
|
sys.path.insert(0, str(TOOLS))
|
|
from proot_profile import report
|
|
|
|
PROGRAM = r'''
|
|
#define _POSIX_C_SOURCE 200809L
|
|
#define PR_NB_SYSNUM 2
|
|
#define PR_void 0
|
|
static const char *stringify_sysnum(unsigned nr) { return nr ? "openat" : "void"; }
|
|
#include "profile.h"
|
|
int main(void) {
|
|
errno = EBUSY;
|
|
if (!dd_profile_enabled()) return errno != EBUSY;
|
|
if (errno != EBUSY) return 1;
|
|
for (int i = 0; i < 40; i++) {
|
|
uint64_t wall = dd_profile_clock(CLOCK_MONOTONIC);
|
|
uint64_t cpu = dd_profile_clock(CLOCK_THREAD_CPUTIME_ID);
|
|
struct timespec wait = {0, 2000000};
|
|
nanosleep(&wait, 0);
|
|
errno = ENOENT;
|
|
dd_profile_record(1, 1, 123, wall, cpu);
|
|
if (errno != ENOENT) return 2;
|
|
if (i == 9) dd_profile_flush();
|
|
}
|
|
dd_profile_next = 0;
|
|
uint64_t wall = dd_profile_clock(CLOCK_MONOTONIC);
|
|
uint64_t cpu = dd_profile_clock(CLOCK_THREAD_CPUTIME_ID);
|
|
struct timespec wait = {0, 2000000};
|
|
nanosleep(&wait, 0);
|
|
dd_profile_record(1, 0, 123, wall, cpu);
|
|
return 0;
|
|
}
|
|
'''
|
|
|
|
|
|
class ProotProfileTest(unittest.TestCase):
|
|
def test_snapshots_replace_instead_of_adding_and_tracers_remain_separate(self):
|
|
with tempfile.TemporaryDirectory() as work:
|
|
path = Path(work) / 'session.log'
|
|
records = [dict(type='total', tracer=1, id=2, syscall='statx', stage='enter',
|
|
calls=n, wall_ns=n * 100, cpu_ns=n * 10, max_ns=100) for n in (3, 8)]
|
|
records.append(dict(records[-1], tracer=2, calls=2))
|
|
records += [dict(type='total', tracer=1, id=3, syscall='openat', stage='enter',
|
|
calls=1, wall_ns=1000, cpu_ns=1000, max_ns=1000)]
|
|
path.write_text('unrelated log\n' + ''.join('prefix proot-profile ' + json.dumps(r) + '\n' for r in records)
|
|
+ 'proot-profile {truncated\n' + 'proot-profile []\n')
|
|
result = report(path)
|
|
self.assertEqual(11, result['handlerCalls'])
|
|
self.assertEqual(3, result['tracers'])
|
|
self.assertEqual(2, result['malformedRecords'])
|
|
self.assertEqual('statx', result['totals'][0]['syscall'])
|
|
self.assertEqual('openat', report(path, 'cpu')['totals'][0]['syscall'])
|
|
|
|
def test_empty_logs_do_not_claim_profiling_ran(self):
|
|
with tempfile.TemporaryDirectory() as work:
|
|
path = Path(work) / 'session.log'
|
|
path.write_text('ordinary log\n')
|
|
self.assertFalse(report(path)['hasCounters'])
|
|
|
|
@unittest.skipUnless(shutil.which('cc'), 'C compiler required')
|
|
def test_real_clocks_bounded_samples_errno_and_periodic_counters(self):
|
|
with tempfile.TemporaryDirectory() as work:
|
|
source = Path(work) / 'profile.c'
|
|
binary = Path(work) / 'profile'
|
|
source.write_text(PROGRAM)
|
|
subprocess.run(['cc', '-Wall', '-Wextra', '-Werror', '-std=c99', '-I', str(TOOLS / 'proot'),
|
|
str(source), '-o', str(binary)], check=True)
|
|
for setting in ('0', ''):
|
|
result = subprocess.run([binary], env=dict(os.environ, PROOT_PROFILE=setting), capture_output=True, check=True)
|
|
self.assertEqual(b'', result.stderr)
|
|
result = subprocess.run([binary], env=dict(os.environ, PROOT_PROFILE='1', PROOT_PROFILE_SLOW_US='1'),
|
|
capture_output=True, check=True)
|
|
rows = [json.loads(line.split(b'proot-profile ', 1)[1]) for line in result.stderr.splitlines()]
|
|
self.assertEqual(33, sum(row['type'] == 'slow' for row in rows))
|
|
self.assertEqual(32, sum(row['type'] == 'slow' and row['stage'] == 'enter' for row in rows))
|
|
self.assertGreater(sum(row['type'] == 'total' for row in rows), 2)
|
|
log = Path(work) / 'session.log'
|
|
log.write_bytes(result.stderr)
|
|
counters = report(log)
|
|
self.assertEqual(41, counters['handlerCalls'])
|
|
self.assertGreater(counters['handlerWallNs'], counters['handlerCpuNs'])
|
|
self.assertEqual(40, counters['totals'][0]['calls'])
|