#!/usr/bin/env python3
"""
同步测试：JLink设置断点 + adb后台启动测试
"""
import pexpect
import sys
import time
import re
import os

JLINK_EXE = "/Applications/SEGGER/JLink_V942/JLinkExe"
ADB_CMD = "adb shell 'su 0 sh -c \"cd /data/local/tmp && ./mcu_upgrade_bug_repro > /data/local/tmp/mcu_test.log 2>&1\"'"

def send_cmd(child, cmd, timeout=10):
    child.sendline(cmd)
    child.expect(r'J-Link>', timeout=timeout)
    return child.before

def parse_reg(regs_output, reg_name):
    for line in regs_output.splitlines():
        if line.startswith(reg_name + ' ='):
            val = line.split('=')[1].strip().split(',')[0]
            return int(val, 16)
    return None

def read_mem32(child, addr):
    out = send_cmd(child, f"mem32 0x{addr:08X},1", timeout=5)
    for line in out.splitlines():
        m = re.search(rf'{addr:08X}\s*=\s*([0-9A-Fa-f]+)', line)
        if m:
            return int(m.group(1), 16)
        m = re.search(rf'{addr:08X}\s+([0-9A-Fa-f]+)', line)
        if m:
            return int(m.group(1), 16)
    return None

def main():
    print("[*] 启动 JLinkExe...")
    child = pexpect.spawn(JLINK_EXE, encoding='utf-8', timeout=30)
    child.logfile_read = sys.stdout

    child.expect(r'J-Link>', timeout=15)
    send_cmd(child, "device HC32A4A8xI")
    send_cmd(child, "si SWD")
    send_cmd(child, "speed 4000")
    child.sendline("connect")
    idx = child.expect([r'Cortex-M4 identified', r'J-Link>'], timeout=30)
    if idx == 0:
        child.expect(r'J-Link>', timeout=10)
    print("\n[+] 目标已连接")

    send_cmd(child, "h")
    # 断点：0x81 处理函数入口，以及关键分支
    bps = [
        (0x0001A350, "0x81 处理函数入口"),
        (0x0001A36C, "前置检查#1 bl 0x1a1a0"),
        (0x0001A370, "前置检查#1 cmp r0,#0"),
        (0x0001A376, "前置检查#2 bl 0x19fec"),
        (0x0001A37A, "前置检查#2 cmp r0,#0"),
        (0x0001A464, "0x81 状态检查 ldrb r0,[r4,#0]"),
        (0x0001A466, "0x81 状态分支 cbz r0,0x1a4c2"),
        (0x0001A4C2, "0x81 错误分支"),
    ]
    for addr, desc in bps:
        send_cmd(child, f"SetBP 0x{addr:08X}")
        print(f"[+] 断点: 0x{addr:08X} ({desc})")

    # 读取当前状态
    print("\n[*] 运行前状态快照:")
    val = read_mem32(child, 0x1FFE999C)
    print(f"[MEM] 0x1FFE999C (upgrade state) = 0x{val:08X}")
    val = read_mem32(child, 0x1FFE19B8)
    print(f"[MEM] 0x1FFE19B8 (msgqueue ptr)  = 0x{val:08X}")

    send_cmd(child, "g")
    print("\n[*] 目标运行中...")

    # 在后台启动adb测试
    print("[*] 启动车机测试（后台）...")
    os.system(ADB_CMD + " &")
    time.sleep(1)

    # 等待断点命中
    hit_count = 0
    max_hits = 10
    while hit_count < max_hits:
        try:
            child.expect(r'(Breakpoint|Halt| halted)', timeout=25)
        except pexpect.exceptions.TIMEOUT:
            print("\n[!] 25秒超时，无断点命中。")
            break

        time.sleep(0.2)
        regs = send_cmd(child, "regs", timeout=5)
        pc = parse_reg(regs, 'PC')
        r0 = parse_reg(regs, 'R0')
        r4 = parse_reg(regs, 'R4')
        r5 = parse_reg(regs, 'R5')

        desc = "未知"
        for addr, d in bps:
            if abs(pc - addr) <= 2:
                desc = d
                break

        print(f"\n[!] ====== 断点命中: PC=0x{pc:08X} ({desc}) ======")
        print(f"[REG] R0=0x{r0:08X} R4=0x{r4:08X} R5=0x{r5:08X}")

        if pc == 0x1A466 and r0 is not None:
            if r0 & 0xFF == 0:
                print("[!] >>> 状态为 0，0x81 将被拒绝！")
            else:
                print(f"[i] 状态=0x{r0&0xFF:02X}，走正常分支")

        if pc == 0x1A4C2:
            print("[!] >>> 0x81 走错误分支！")

        if pc == 0x1A370 or pc == 0x1A37A:
            print(f"[i] 前置检查返回，R0=0x{r0:08X}")
            if r0 != 0:
                print("[!] >>> 前置检查失败（R0!=0）！")
            else:
                print("[i] 前置检查通过（R0==0）")

        hit_count += 1
        send_cmd(child, "g")
        print("[*] 继续运行...")

    # 读取车机测试输出
    print("\n[*] 读取车机测试日志...")
    time.sleep(3)
    os.system("adb shell 'cat /data/local/tmp/mcu_test.log' 2>/dev/null")

    print("\n[*] 清理...")
    send_cmd(child, "h")
    send_cmd(child, "ClrBP", timeout=3)
    send_cmd(child, "g", timeout=3)
    child.sendline("q")
    try:
        child.expect(pexpect.EOF, timeout=5)
    except:
        child.terminate(force=True)
    print("[+] 完成")

if __name__ == "__main__":
    main()
