Skip to content
Closed
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
73 changes: 73 additions & 0 deletions Tests/LibWebView/test-webdriver-session-history.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@
EVENT_TIMEOUT_SECONDS = 30
WEBDRIVER_REQUEST_TIMEOUT_SECONDS = 60

forensics_root_pid = None


class TestPageServer(http.server.ThreadingHTTPServer):
def __init__(self, server_address, request_handler_class):
Expand Down Expand Up @@ -695,9 +697,75 @@ def wait_for_port(port, timeout=EVENT_TIMEOUT_SECONDS):
raise RuntimeError(f"Timed out waiting for port {port}")


def read_proc_file(path):
try:
with open(path, "rb") as f:
return f.read().decode("utf-8", "replace").strip()
except OSError:
return None


def descendant_pids(root_pid):
children = {}
for entry in os.listdir("/proc"):
if not entry.isdigit():
continue
stat = read_proc_file(f"/proc/{entry}/stat")
if stat is None:
continue
close_paren = stat.rfind(")")
fields = stat[close_paren + 2 :].split()
if len(fields) < 2:
continue
children.setdefault(int(fields[1]), []).append(int(entry))
pids = []
queue = [root_pid]
while queue:
pid = queue.pop()
pids.append(pid)
queue.extend(children.get(pid, []))
return pids


def dump_process_tree_forensics(reason):
if forensics_root_pid is None or not os.path.isdir("/proc"):
return
print(f"=== process forensics: {reason} ===", file=sys.stderr, flush=True)
for pid in descendant_pids(forensics_root_pid):
comm = read_proc_file(f"/proc/{pid}/comm") or "?"
cmdline = (read_proc_file(f"/proc/{pid}/cmdline") or "").replace("\x00", " ").strip()
print(f"pid {pid} ({comm}): {cmdline}", file=sys.stderr, flush=True)
task_dir = f"/proc/{pid}/task"
try:
tids = sorted(int(task) for task in os.listdir(task_dir) if task.isdigit())
except OSError:
continue
for tid in tids:
thread_comm = read_proc_file(f"{task_dir}/{tid}/comm") or "?"
stat = read_proc_file(f"{task_dir}/{tid}/stat") or ""
close_paren = stat.rfind(")")
state = stat[close_paren + 2 :].split()[0] if close_paren >= 0 else "?"
wchan = read_proc_file(f"{task_dir}/{tid}/wchan") or "?"
syscall = read_proc_file(f"{task_dir}/{tid}/syscall") or "?"
print(
f" tid {tid} ({thread_comm}) state={state} wchan={wchan} syscall={syscall}",
file=sys.stderr,
flush=True,
)
print(f"=== end process forensics: {reason} ===", file=sys.stderr, flush=True)


def dump_process_tree_forensics_best_effort(reason):
try:
dump_process_tree_forensics(reason)
except Exception as error:
print(f"process forensics failed: {error}", file=sys.stderr, flush=True)


def wait_for_event(event, label, timeout=EVENT_TIMEOUT_SECONDS):
if event.wait(timeout=timeout):
return
dump_process_tree_forensics_best_effort(f"timed out waiting for {label}")
raise RuntimeError(f"Timed out waiting for {label}")


Expand All @@ -724,6 +792,9 @@ def request_raw(webdriver_port, method, path, body=None):
connection.request(method, path, encoded_body, headers)
response = connection.getresponse()
response_body = response.read().decode()
except TimeoutError:
dump_process_tree_forensics_best_effort(f"{method} {path} timed out")
raise
finally:
connection.close()

Expand Down Expand Up @@ -2696,6 +2767,8 @@ def run_test(webdriver_binary):
text=True,
env=env,
)
global forensics_root_pid
forensics_root_pid = webdriver.pid

session_id = None
failed = False
Expand Down