mirror of
https://gitee.com/xmz7891/ykxmz.git
synced 2026-10-18 10:10:43 +00:00
加两个运维辅助脚本
_build/watch_stability.py 连续打公网统计可用率(验证隧道加固效果)
_build/desktop_state.py 列出当前可见窗口 + 输入桌面名
(排查"桌面消失/一片蓝"这类问题用)
这两个是查 2.1.1 / 2.1.2 那两个问题时写的,以后还会用到。
顺便记一笔当时的排查结论(2026-10-08):
用户报"桌面消失、一片蓝、有个蓝色窗口",查下来:
* 输入桌面是 Default(正常),explorer 在跑
* 那 9 个"全屏窗口"全是 Windows 自己的 UWP 组件
(开始菜单 / 搜索 / 锁屏 / 输入法),程序枚举时会误报全屏
* 真正开着的是「设置」App 和任务管理器
* 自启项里没有 ykxmz,也没有残留的隐藏桌面或测试进程
* 18:42:41 有一次**非正常关机**(Kernel-Power 41 / EventLog 6008),
19:58 重启。没有任何蓝屏或硬件错误记录 ——
最符合"掉电或硬挂",不是软件崩溃。
This commit is contained in:
@@ -0,0 +1,18 @@
|
||||
加两个运维辅助脚本
|
||||
|
||||
_build/watch_stability.py 连续打公网统计可用率(验证隧道加固效果)
|
||||
_build/desktop_state.py 列出当前可见窗口 + 输入桌面名
|
||||
(排查"桌面消失/一片蓝"这类问题用)
|
||||
|
||||
这两个是查 2.1.1 / 2.1.2 那两个问题时写的,以后还会用到。
|
||||
|
||||
顺便记一笔当时的排查结论(2026-10-08):
|
||||
用户报"桌面消失、一片蓝、有个蓝色窗口",查下来:
|
||||
* 输入桌面是 Default(正常),explorer 在跑
|
||||
* 那 9 个"全屏窗口"全是 Windows 自己的 UWP 组件
|
||||
(开始菜单 / 搜索 / 锁屏 / 输入法),程序枚举时会误报全屏
|
||||
* 真正开着的是「设置」App 和任务管理器
|
||||
* 自启项里没有 ykxmz,也没有残留的隐藏桌面或测试进程
|
||||
* 18:42:41 有一次**非正常关机**(Kernel-Power 41 / EventLog 6008),
|
||||
19:58 重启。没有任何蓝屏或硬件错误记录 ——
|
||||
最符合"掉电或硬挂",不是软件崩溃。
|
||||
@@ -0,0 +1,120 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
r'''看当前桌面/窗口的真实状态 —— 诊断"桌面消失、一片蓝"。
|
||||
|
||||
用户报:"我现在桌面消失了,电脑一片蓝,然后还有个蓝色的窗口"。
|
||||
|
||||
已知:
|
||||
18:42:41 机器异常关机(Kernel-Power 41 / EventLog 6008,无蓝屏记录)
|
||||
19:58:23 重新启动
|
||||
20:01:37 explorer 起来
|
||||
自启项里没有 ykxmz,当前也没有 ykxmz 在跑
|
||||
|
||||
要查的是:现在屏幕上到底有什么、是不是有全屏窗口盖住了桌面。
|
||||
'''
|
||||
from __future__ import annotations
|
||||
|
||||
import ctypes
|
||||
import ctypes.wintypes as wt
|
||||
import subprocess
|
||||
|
||||
user32 = ctypes.WinDLL("user32", use_last_error=True)
|
||||
|
||||
EnumWindows = user32.EnumWindows
|
||||
EnumWindows.argtypes = [ctypes.WINFUNCTYPE(wt.BOOL, wt.HWND, wt.LPARAM),
|
||||
wt.LPARAM]
|
||||
|
||||
|
||||
class RECT(ctypes.Structure):
|
||||
_fields_ = [("left", wt.LONG), ("top", wt.LONG),
|
||||
("right", wt.LONG), ("bottom", wt.LONG)]
|
||||
|
||||
|
||||
def windows() -> list:
|
||||
out = []
|
||||
|
||||
def cb(hwnd, _lp):
|
||||
if not user32.IsWindowVisible(hwnd):
|
||||
return True
|
||||
title = ctypes.create_unicode_buffer(512)
|
||||
user32.GetWindowTextW(hwnd, title, 512)
|
||||
cls = ctypes.create_unicode_buffer(256)
|
||||
user32.GetClassNameW(hwnd, cls, 256)
|
||||
r = RECT()
|
||||
user32.GetWindowRect(hwnd, ctypes.byref(r))
|
||||
pid = wt.DWORD()
|
||||
user32.GetWindowThreadProcessId(hwnd, ctypes.byref(pid))
|
||||
w, h = r.right - r.left, r.bottom - r.top
|
||||
if w > 80 and h > 80:
|
||||
out.append({"hwnd": hwnd, "title": title.value, "cls": cls.value,
|
||||
"pid": pid.value, "w": w, "h": h,
|
||||
"x": r.left, "y": r.top})
|
||||
return True
|
||||
|
||||
EnumWindows(ctypes.WINFUNCTYPE(wt.BOOL, wt.HWND, wt.LPARAM)(cb), 0)
|
||||
return out
|
||||
|
||||
|
||||
def proc_name(pid: int) -> str:
|
||||
try:
|
||||
r = subprocess.run(
|
||||
["powershell", "-NoProfile", "-Command",
|
||||
"(Get-Process -Id %d -EA SilentlyContinue).ProcessName" % pid],
|
||||
capture_output=True, text=True, timeout=8)
|
||||
return (r.stdout or "").strip() or "?"
|
||||
except Exception: # noqa: BLE001
|
||||
return "?"
|
||||
|
||||
|
||||
def main() -> int:
|
||||
sw = user32.GetSystemMetrics(0)
|
||||
sh = user32.GetSystemMetrics(1)
|
||||
print(" 屏幕: %d x %d" % (sw, sh))
|
||||
print(" 当前输入桌面: %s" % _desktop_name())
|
||||
print()
|
||||
|
||||
ws = windows()
|
||||
ws.sort(key=lambda w: w["w"] * w["h"], reverse=True)
|
||||
print(" 可见窗口 %d 个(按面积排序,前 20):" % len(ws))
|
||||
for w in ws[:20]:
|
||||
full = w["w"] >= sw - 4 and w["h"] >= sh - 4
|
||||
pn = proc_name(w["pid"])
|
||||
print(" %s %-14s pid=%-6d %dx%d @(%d,%d)"
|
||||
% ("★全屏" if full else " ", pn, w["pid"],
|
||||
w["w"], w["h"], w["x"], w["y"]))
|
||||
print(" 类=%-22s 标题=%r" % (w["cls"][:22], w["title"][:60]))
|
||||
print()
|
||||
fulls = [w for w in ws if w["w"] >= sw - 4 and w["h"] >= sh - 4]
|
||||
if fulls:
|
||||
print(" ⚠ 有 %d 个全屏窗口 —— 下面这些就是盖住桌面的嫌疑:" % len(fulls))
|
||||
for w in fulls:
|
||||
print(" pid=%d (%s) 类=%s 标题=%r"
|
||||
% (w["pid"], proc_name(w["pid"]), w["cls"], w["title"][:50]))
|
||||
else:
|
||||
print(" ✓ 没有全屏窗口盖着")
|
||||
print()
|
||||
ex = subprocess.run(["powershell", "-NoProfile", "-Command",
|
||||
"Get-Process explorer -EA SilentlyContinue | "
|
||||
"Select-Object -First 1 -ExpandProperty Id"],
|
||||
capture_output=True, text=True, timeout=10)
|
||||
print(" explorer PID: %s" % ((ex.stdout or "").strip() or "**没在跑**"))
|
||||
return 0
|
||||
|
||||
|
||||
def _desktop_name() -> str:
|
||||
"""当前输入桌面的名字。Default = 正常,Winlogon = 锁屏。"""
|
||||
h = user32.OpenInputDesktop(0, False, 0x0001)
|
||||
if not h:
|
||||
return "(拿不到 —— 可能锁屏或没有交互桌面)"
|
||||
try:
|
||||
sb = ctypes.create_unicode_buffer(256)
|
||||
need = wt.DWORD()
|
||||
ok = user32.GetUserObjectInformationW(h, 2, sb, 512,
|
||||
ctypes.byref(need))
|
||||
return sb.value if ok else "(拿不到名字)"
|
||||
finally:
|
||||
user32.CloseDesktop(h)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import sys
|
||||
sys.exit(main())
|
||||
@@ -0,0 +1,85 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
r'''稳定性观测:连续打公网,统计可用率。
|
||||
|
||||
用途:验证 2.1.2 的隧道加固(协议 auto + 重试拉大)真的有效。
|
||||
2.1.1 之前是"过一阵就 530",所以只看一次 200 说明不了问题,
|
||||
要**连续观测一段时间**。
|
||||
|
||||
用法:
|
||||
python _build/watch_stability.py # 默认 20 次、间隔 15 秒
|
||||
python _build/watch_stability.py 40 20 # 40 次、间隔 20 秒
|
||||
'''
|
||||
from __future__ import annotations
|
||||
|
||||
import sys
|
||||
import time
|
||||
import urllib.error
|
||||
import urllib.request
|
||||
|
||||
URL = "https://relay.xxmxxs.top/v1/health"
|
||||
|
||||
|
||||
def once(timeout: float = 15.0) -> tuple:
|
||||
"""返回 (状态码文本, 耗时秒)。"""
|
||||
t0 = time.time()
|
||||
try:
|
||||
req = urllib.request.Request(
|
||||
URL, headers={"Cache-Control": "no-cache",
|
||||
"User-Agent": "ykxmz-watch"})
|
||||
with urllib.request.urlopen(req, timeout=timeout) as resp:
|
||||
resp.read(64)
|
||||
return str(resp.status), time.time() - t0
|
||||
except urllib.error.HTTPError as exc:
|
||||
try:
|
||||
exc.read(64)
|
||||
except Exception: # noqa: BLE001
|
||||
pass
|
||||
return str(exc.code), time.time() - t0
|
||||
except Exception as exc: # noqa: BLE001
|
||||
return "无响应(%s)" % type(exc).__name__, time.time() - t0
|
||||
|
||||
|
||||
def main() -> int:
|
||||
times = int(sys.argv[1]) if len(sys.argv) > 1 else 20
|
||||
gap = float(sys.argv[2]) if len(sys.argv) > 2 else 15.0
|
||||
|
||||
print("=" * 58)
|
||||
print(" 公网稳定性观测:%d 次,间隔 %.0f 秒(约 %.0f 分钟)"
|
||||
% (times, gap, times * gap / 60))
|
||||
print("=" * 58)
|
||||
|
||||
codes = []
|
||||
durs = []
|
||||
for i in range(1, times + 1):
|
||||
code, dur = once()
|
||||
codes.append(code)
|
||||
durs.append(dur)
|
||||
mark = "✓" if code == "200" else "✗"
|
||||
print(" %s 第 %2d 次: %-8s %.2fs" % (mark, i, code, dur))
|
||||
if i < times:
|
||||
time.sleep(gap)
|
||||
|
||||
ok = sum(1 for c in codes if c == "200")
|
||||
print()
|
||||
print("=" * 58)
|
||||
print(" 200 的次数: %d / %d" % (ok, times))
|
||||
print(" 可用率: %.1f%%" % (100.0 * ok / max(1, times)))
|
||||
if durs:
|
||||
sd = sorted(durs)
|
||||
print(" 延迟 中位: %.2fs P90: %.2fs 最大: %.2fs"
|
||||
% (sd[len(sd) // 2], sd[min(len(sd) - 1, int(len(sd) * 0.9))],
|
||||
sd[-1]))
|
||||
print()
|
||||
if ok == times:
|
||||
print(" ✅ 全程 200 —— 加固有效")
|
||||
elif ok >= times * 0.9:
|
||||
print(" ⚠ 偶尔失败(%d 次)—— 比加固前好,但还没完全稳"
|
||||
% (times - ok))
|
||||
else:
|
||||
print(" ❌ 可用率偏低(%d 次失败)—— 还要继续查"
|
||||
% (times - ok))
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
Reference in New Issue
Block a user