乐于分享
好东西不私藏

探究OpenClaw+CPython解释器+OSR堆栈替换(net10)

探究OpenClaw+CPython解释器+OSR堆栈替换(net10)
批判性探究OpenClaw
从头开始。
OpenClaw启动的时候一般都是运行如下命令,启动了整个龙虾。
OpenClaw GateWay
这个命令是如何通过代码运行的呢?OpenClaw是一个二进制文件,而它实际上指向的一个软链接文件openclaw.mjs。这个文件才是龙虾运行的第一个文件。
tang@tang-vm:~$ ls -la /home/tang/.nvm/versions/node/v22.22.0/bin/openclawlrwxrwxrwx 1 tang tang 41  /home/tang/.nvm/versions/node/v22.22.0/bin/openclaw -> ../lib/node_modules/openclaw/openclaw.mjs
这里说点题外话,有一个package.json文件。它在npm install的时候会读取里面的bin字段,把openclaw.mjs创建为openclaw二进制的软链接。然后就形成了以上过程。
tang@tang-vm:~$ grep -A3 '"bin"/home/tang/.nvm/versions/node/v22.22.0/lib/node_modules/openclaw/package.json  "bin": {    "openclaw""openclaw.mjs"  },  "directories": {
openclaw.mjs里面调用了entry.ts这个是程序的入口,OpenClaw本身版本号检查在这里。这里import version.ts。可以理解VERSION是这个version.js里面的一个常量。
// home/tang/opt/openclawsource/openclaw/src/entry.ts function tryHandleRootVersionFastPath(argvstring[]): boolean {    if (!isRootVersionInvocation(argv)) {      return false;    }    import("./version.js")      .then(({ VERSION }) => {        console.log(VERSION);      })
以及调用了runCli(类似于C的main)
 if (!tryHandleRootVersionFastPath(process.argv) && !tryHandleRootHelpFastPath(process.argv)) {      import("./cli/run-main.js")        .then(({ runCli }) => runCli(process.argv))
runcli里面OpenClaw检查nodejs版本是否大于22
// /home/tang/opt/openclawsource/openclaw/src/infra/runtime-guard.tsexportfunctionassertSupportedRuntime(  runtimeRuntimeEnv = defaultRuntime,  detailsRuntimeDetails = detectRuntime(),): void {//省略   runtime.error(    ["openclaw requires Node >=22.12.0.",`Detected: ${runtimeLabel} (exec: ${execLabel}).`,`PATH searched: ${details.pathEnv}`,"Install Node: https://nodejs.org/en/download","Upgrade Node and re-run openclaw.",    ].join("\n"),  );  runtime.exit(1);}
比如说我们安装了QQ插件,这个runCli此时就会启动QQ机器人(Starting gateway),初始化账号(startAccount)
// vim ~/.openclaw/extensions/openclaw-qqbot/src/channel.ts // 追踪runcli调用QQ堆栈如下   gateway: {    startAccountasync (ctx) => {      const { account, abortSignal, log, cfg } = ctx;      console.trace("QQ starting gateway************************************************************");      log?.info(`[qqbot:${account.accountId}] Starting gateway — appId=${account.appId}, enabled=${account.enabled}, name=${account.name ?? "unnamed"}`);      console.log(`[qqbot:channel] startAccount: accountId=${account.accountId}, appId=${account.appId}, secretSource=${account.secretSource}`); //它这里对应的QQ日志 08:26:25 [qqbot] [qqbot:defaultStarting gateway — appId=xxx, enabled=true, name=unnamed08:26:25 [qqbot:channel] startAccount: accountId=default, appId=xxx, secretSource=config
进行一个websocket链接
// vim /home/tang/.openclaw/extensions/openclaw-qqbot/src/gateway.ts    ws.on("open"() => {        log?.info(`[qqbot:${account.accountId}] WebSocket connected`);        isConnecting = false// 连接完成,释放锁        reconnectAttempts = 0// 连接成功,重置重试计数        lastConnectTime = Date.now(); // 记录连接时间        // 启动消息处理器(异步处理,防止阻塞心跳)        startMessageProcessor(handleMessage);        // P1-1: 启动后台 Token 刷新        startBackgroundTokenRefresh(account.appId, account.clientSecret, {          log: log as { info(msgstring) => voiderror(msgstring) => voiddebug?: (msgstring) => void },        });      });
如此OpenClaw就跟QQ建立了联系。以后QQ发送消息,都有这个插件来接受。如果调用AI的话还是OpenClaw,具体的就是QQ插件把QQ发送过来的消息交给OpenClaw,OpenClaw调用AI大模型返回消息,最后把消息发送给QQ。
// vim /home/tang/.openclaw/extensions/openclaw-qqbot/src/gateway.tsconst dispatchPromise = pluginRuntime.channel.reply.dispatchReplyWithBufferedBlockDispatcher({ctx: ctxPayload,
这里其实还有个问题。QQ插件注册的问题,它是怎么融入OpenClaw的?
OpenClaw启动加载插件,会查看注册,比如QQ插件
$ cat ~/.openclaw/extensions/openclaw-qqbot/index.tsimport type { OpenClawPluginApi } from "openclaw/plugin-sdk";import { emptyPluginConfigSchema } from "openclaw/plugin-sdk";import { qqbotPlugin } from "./src/channel.js";import { setQQBotRuntime } from "./src/runtime.js";const plugin = {  id"openclaw-qqbot",  name"QQ Bot",  description"QQ Bot channel plugin",  configSchemaemptyPluginConfigSchema(),  register(apiOpenClawPluginApi) {    setQQBotRuntime(api.runtime);    api.registerChannel({ plugin: qqbotPlugin });  },};
它这个地方会注册。当openclaw发现有注册的QQ插件,
startGateway函数会调用QQ API建立联系
  // ~/.openclaw/extensions/openclaw-qqbot/src/channel.ts  await startGateway({        account,        abortSignal,        cfg,        log,        onReady() => {          log?.info(`[qqbot:${account.accountId}] Gateway ready`);          ctx.setStatus({            ...ctx.getStatus(),            runningtrue,            connectedtrue,            lastConnectedAtDate.now(),          });        },        onError(error) => {          log?.error(`[qqbot:${account.accountId}] Gateway error: ${error.message}`);          ctx.setStatus({            ...ctx.getStatus(),            lastError: error.message,          });        },      });
startGateWay实现在
// ~/.openclaw/extensions/openclaw-qqbot/src/gateway.tsexport async function startGateway(ctx: GatewayContext): Promise<void> {  const { account, abortSignal, cfg, onReady, onError, log } = ctx;  if (!account.appId || !account.clientSecret) {    throw new Error("QQBot not configured (missing appId or clientSecret)");  }  // 启动环境诊断(首次连接时执行)  const diag = await runDiagnostics();  if (diag.warnings.length > 0) {    for (const w of diag.warnings) {      log?.info(`[qqbot:${account.accountId}${w}`);    }  }  // 后台版本检查(供 /bot-version、/bot-upgrade 指令被动查询)  triggerUpdateCheck(log);  // 初始化 API 配置(markdown 支持)  initApiConfig({    markdownSupport: account.markdownSupport,  });  log?.info(`[qqbot:${account.accountId}] API config: markdownSupport=${account.markdownSupport === true}`);
从你输入一个消息(动作)开始,到返回。OpenClaw历经了什么,本篇看下。任何动作都可以看做一个任务,所以我们先从任务开始。
OpenClaw函数drainLane会检查并发槽里的任务是否为空,如果是则执行任务。
// src/process/command-queue.tsfunction drainLane(lane) {    //用于堆栈跟踪     console.trace("drainlane*********************************************************************标签");    const state = getLaneState(lane);    if (state.draining) {        if (state.activeTaskIds.size === 0 && state.queue.length > 0) diag.warn(`drainLane blocked: lane=${lane} draining=true active=0 queue=${state.queue.length}`);        return;    }    const entry = state.queue.shift();      const result = await entry.task();  //调用任务}
注意这里的调用任务(entry.task())大约是括号执行
 // src/process/command-queue.ts (async () => {                    const startTime = Date.now();                    try {                        const result = await entry.task();                        if (completeTask(state, taskId, taskGeneration)) {                            diag.debug(`lane task done: lane=${lane} durationMs=${Date.now() - startTime} active=${state.activeTaskIds.size} queued=${state.queue.length}`);                            pump();                        }                        entry.resolve(result);                    } catch (err) {                        const completedCurrentGeneration = completeTask(state, taskId, taskGeneration);                        if (!(lane.startsWith("auth-probe:") || lane.startsWith("session:probe-"))) diag.error(`lane task error: lane=${lane} durationMs=${Date.now() - startTime} error="${String(err)}"`);                        if (completedCurrentGeneration) pump();                        entry.reject(err);                    }                })();
entry.task()任务会进入以下函数
/*runEmbeddedAttempt(单次尝试)        ├── 解析工作目录        ├── 初始化沙箱        ├── 加载 skills        ├── 加载 bootstrap 文件        ├── 创建工具(createOpenClawCodingTools)        ├── buildEmbeddedSystemPrompt(构建 system prompt)        └── 交给 pi-agent-core runLoop 驱动模型*/// src/agents/pi-embedded-runner/run.ts        async function runEmbeddedPiAgent(params) {        const attempt = await runEmbeddedAttempt({sessionId: params.sessionId,sessionKey: params.sessionKey...        }
runEmbeddedAttempt里面会调用
buildEmbeddedSystemPrompt构建提示词。同时它也会调用工具

toToolDefinitions。

// src/agents/pi-embedded-runner/run.tsasync function runEmbeddedAttempt(params) {    const appendPrompt = buildEmbeddedSystemPrompt({});    //中间省略         const { builtInTools, customTools } = splitSdkTools({                tools,                sandboxEnabled: !!sandbox?.enabled            });    }----------------------------------------function splitSdkTools(options) {    const { tools } = options;    return {        builtInTools: [],        customToolstoToolDefinitions(tools)    };}

toToolDefinitions就会调用工具跟大模型进行一个对话了

// src/agents/pi-tool-definition-adapter.tsfunction toToolDefinitions(tools) {    // 便于堆栈追踪     console.trace("toToolDefinitionstoToolDefinitions********************************************标签");     //中间省略                     return normalizeToolExecutionResult({                        toolName: normalizedName,/                        // 这个地方调用工具                        result: await tool.execute(toolCallId, executeParams, signal, onUpdate)                    });

比如我提问:

帮我回忆下我们之前聊过什么或者 调用memory_search工具搜索下:伊朗

那么此时就会调用工具memory_search,代码

//src/memory/manager.tsasync search(query, opts) {          // 用于堆栈追踪          console.trace("**************************************************************search到这里了 query=" + query);

比如说我们要跟踪记忆数据库的话,可以

// src/memory/manager-sync-ops.tsopenDatabaseAtPath(dbPath) {        console.trace("**************************************************************到这里了");        debugger;        ensureDir(path.dirname(dbPath));        const { DatabaseSync } = requireNodeSqlite();        return new DatabaseSync(dbPath, { allowExtension: this.settings.store.vector.enabled });    }

这里面有一个构造函数的实例化数据库

constructor(params) {this.db = this.openDatabase();}

这个构造函数是在下面被初始化的

// src/agents/tools/memory-tool.tsfunction createMemorySearchTool(options) {    const ctx = resolveMemoryToolContext(options);   // 中间省略                 });                const rawResults = await manager.search(query, { //要跟踪到这个代码 它的堆栈如下  async search(query, opts) {        console.trace("**************************************************************search到这里了 query=" + query);

我们的drainLane是何时被调用的呢?追踪如下

function drainLane(lane) {    console.trace("drainlane*********************************************************************标签");

最终的第一个堆栈是

// src/cli/gateway-cli/run.tsfunction addGatewayRunCommand(cmd) {    return cmd.option("--port <port>""Port for the gateway WebSocket").option("--bind <mode>""Bind mode (\"loopback\"|\"lan\"|\"tailnet\"|\"auto\"|\"custom\"). Defaults to config gateway.bind (or loopback).").option("--token <token>""Shared token required in connect.params.auth.token (default: OPENCLAW_GATEWAY_TOKEN env if set)").option("--auth <mode>", `Gateway auth mode (${formatModeChoices(GATEWAY_AUTH_MODES)})`).option("--password <password>""Password for auth mode=password").option("--tailscale <mode>", `Tailscale exposure mode (${formatModeChoices(GATEWAY_TAILSCALE_MODES)})`).option("--tailscale-reset-on-exit""Reset Tailscale serve/funnel configuration on shutdown"false).option("--allow-unconfigured""Allow gateway start without gateway.mode=local in config"false).option("--dev""Create a dev config + workspace if missing (no BOOTSTRAP.md)"false).option("--reset""Reset dev config + credentials + sessions + workspace (requires --dev)"false).option("--force""Kill any existing listener on the target port before starting"false).option("--verbose""Verbose logging to stdout/stderr"false).option("--claude-cli-logs""Only show claude-cli logs in the console (includes stdout/stderr)"false).option("--ws-log <style>""WebSocket log style (\"auto\"|\"full\"|\"compact\")""auto").option("--compact""Alias for \"--ws-log compact\""false).option("--raw-stream""Log raw model stream events to jsonl"false).option("--raw-stream-path <path>""Raw stream jsonl path").action(async (opts, command) => {        await runGatewayCommand$1(resolveGatewayRunOptions(opts, command));    });}

这实际上是一个命令行的传入。

比如:OpenClaw GateWay --port 8888等。

搞了这么久,还没看到跟大模型交互。实际的交互代码如下:

cat ~/opt/openclawsource/openclaw/node_modules/.pnpm/@mariozechner+pi-agent-core@0.55.3_ws@8.19.0_zod@4.3.6/node_modules/@mariozechner/pi-agent-core/dist/agent-loop.jsasync function streamAssistantResponse(context, config, signal, stream, streamFn) {    streamFunction() //这里跟大模型交互 可以追踪  }

那么大致

drainLane--》await entry.task()--》runEmbeddedPiAgent--》runEmbeddedAttempt--》buildEmbeddedSystemPrompt和splitSdkTools--》toToolDefinitions--》 tool.execute--》manager.search(query--》
整个OpenClaw的QQ插件运行过程梳理完毕,大致是OpenClaw启动,然后QQ初始化,连接等待消息,接受消息转给OpenClaw让其大模型处理,然后返回给QQ的一个过程。
我们可以模拟下QQ机器人和OpenClaw对话过程,代码大约100多行,小巧精悍。
import asyncioimport aiohttpimport jsonimport time# 从 OpenClaw 配置里拿这两个值APP_ID = "xxx"CLIENT_SECRET = "xxxxx"# ===== 1. 获取 Access Token =====async def get_access_token(session):    resp = await session.post(        "https://bots.qq.com/app/getAppAccessToken",        json={"appId": APP_ID, "clientSecret": CLIENT_SECRET}    )    data = await resp.json()    return data["access_token"]# ===== 2. 获取 WebSocket 网关地址 =====async def get_gateway_url(session, token):    resp = await session.get(        "https://api.sgroup.qq.com/gateway",        headers={"Authorization"f"QQBot {token}"}    )    data = await resp.json()    return data["url"]# ===== 3. 发送消息给用户 =====async def send_message(session, token, user_id, content, msg_id):    await session.post(        f"https://api.sgroup.qq.com/v2/users/{user_id}/messages",        headers={"Authorization"f"QQBot {token}"},        json={            "content": content,            "msg_type"0,            "msg_id": msg_id,            "msg_seq"int(time.time()),        }    )# ===== 4. 处理收到的消息 =====async def handle_message(session, token, event):    user_id = event["author"]["id"]    content = event["content"].strip()    msg_id = event["id"]    print(f"收到消息 from {user_id}{content}")    # 在这里写你的业务逻辑    # reply = f"你说的是:{content}"    # Please install OpenAI SDK first: `pip3 install openai`    import os    from openai import OpenAI    client = OpenAI(        api_key="sk-xxxxxx",        base_url="https://api.deepseek.com")    response = client.chat.completions.create(        model="deepseek-chat",        messages=[            {"role""system""content""You are a helpful assistant"},            {"role""user""content": content},        ],        stream=False    )    # print(response.choices[0].message.content)    await send_message(session, token, user_id, response.choices[0].message.content, msg_id)# ===== 5. WebSocket 主循环 =====async def main():    async with aiohttp.ClientSession() as session:        token = await get_access_token(session)        print(f"Token 获取成功")        gateway_url = await get_gateway_url(session, token)        print(f"Gateway: {gateway_url}")        async with session.ws_connect(gateway_url) as ws:            heartbeat_interval = None            async for msg in ws:                data = json.loads(msg.data)                op = data.get("op")                t  = data.get("t")                d  = data.get("d", {})                # OP=10: 服务器发来 Hello,开始心跳                if op == 10:                    heartbeat_interval = d["heartbeat_interval"] / 1000                    asyncio.create_task(heartbeat(ws, heartbeat_interval))                    # 发送 Identify                    await ws.send_json({                        "op"2,                        "d": {                            "token"f"QQBot {token}",                            "intents"1107300352,  # 群聊+私信+频道,和 OpenClaw 一样                            "shard": [01],                        }                    })                # OP=0: 事件派发                elif op == 0:                    if t == "C2C_MESSAGE_CREATE":       # 私聊消息                        await handle_message(session, token, d)                    elif t == "GROUP_AT_MESSAGE_CREATE"# 群@消息                        print(f"群消息: {d}")                    elif t == "READY":                        print(f"Bot 已就绪: {d['user']['username']}")                # OP=11: 心跳 ACK                elif op == 11:                    print("心跳 ACK")# ===== 心跳 =====async def heartbeat(ws, interval):    while True:        await asyncio.sleep(interval)        await ws.send_json({"op"1"d"None})        print("发送心跳")asyncio.run(main())
主要是通过QQ发送消息,这段程序接收到了以后,转发给deepseek Agent,把agent的结果发送给QQ,完成整个对话。
如果加上操控电脑
import asyncioimport aiohttpimport jsonimport time# 从 OpenClaw 配置里拿这两个值APP_ID = "xxxx"CLIENT_SECRET = "xxxxxxx"# ===== 1. 获取 Access Token =====async def get_access_token(session):    resp = await session.post(        "https://bots.qq.com/app/getAppAccessToken",        json={"appId": APP_ID, "clientSecret": CLIENT_SECRET}    )    data = await resp.json()    return data["access_token"]# ===== 2. 获取 WebSocket 网关地址 =====async def get_gateway_url(session, token):    resp = await session.get(        "https://api.sgroup.qq.com/gateway",        headers={"Authorization"f"QQBot {token}"}    )    data = await resp.json()    return data["url"]# ===== 3. 发送消息给用户 =====async def send_message(session, token, user_id, content, msg_id):    await session.post(        f"https://api.sgroup.qq.com/v2/users/{user_id}/messages",        headers={"Authorization"f"QQBot {token}"},        json={            "content": content,            "msg_type"0,            "msg_id": msg_id,            "msg_seq"int(time.time()),        }    )import inspect, jsonfrom openai import OpenAI# ---- helper ----def make_tool(func):    sig = inspect.signature(func)    props = {        name: {"type""string""description": name}        for name in sig.parameters    }    return {        "type""function",        "function": {            "name": func.__name__,            "description": func.__doc__ or func.__name__,            "parameters": {                "type""object",                "properties": props,                "required"list(props.keys())            }        }    }import subprocessimport os# ---- 定义工具函数 ----def get_weather(city: str) -> str:    """获取指定城市的天气"""    return f"{city} 今天晴天 25°C"def calculator(expression: str) -> str:    """计算数学表达式"""    return str(eval(expression))def open_cmd(command: str = "") -> str:    """打开cmd窗口,可选传入要执行的命令"""    if command:        os.system(f'start cmd /k "{command}"')        return f"已打开cmd并执行: {command}"    else:        os.system("start cmd")        return "已打开cmd窗口"def run_cmd(command: str) -> str:    """在后台执行cmd命令并返回结果"""    result = subprocess.run(        command, shell=True,        capture_output=True, text=True, timeout=30    )    return result.stdout or result.stderr or "执行完成"# ---- 注册工具 ----tool_funcs = [get_weather, calculator,open_cmd,run_cmd]tools = [make_tool(f) for f in tool_funcs]tool_map = {f.__name__: f for f in tool_funcs}# ===== 4. 处理收到的消息 =====async def handle_message(session, token, event):    user_id = event["author"]["id"]    content = event["content"].strip()    msg_id = event["id"]    print(f"收到消息 from {user_id}{content}")    # 在这里写你的业务逻辑    # Please install OpenAI SDK first: `pip3 install openai`    import os    from openai import OpenAI    client = OpenAI(        api_key="sk-xxxxxxxx",        base_url="https://api.deepseek.com")    messages = [        {"role""system""content""You are a helpful assistant"},        {"role""user""content": content},    ]    response = client.chat.completions.create(        model="deepseek-chat",        messages=messages,        tools=tools,        tool_choice="auto",        stream=False    )    # print    msg = response.choices[0].message    if msg.tool_calls:        messages.append(msg)        for tool_call in msg.tool_calls:            func_name = tool_call.function.name            func_args = json.loads(tool_call.function.arguments)            result = tool_map[func_name](**func_args)            messages.append({                "role""tool",                "tool_call_id": tool_call.id,                "content": result            })        final = client.chat.completions.create(            model="deepseek-chat",            messages=messages,            stream=False        )        # print(final.choices[0].message.content)        await send_message(session, token, user_id, final.choices[0].message.content, msg_id)        print(f"发送出去的消息:{final.choices[0].message.content}")    else:        #(msg.content)        await send_message(session, token, user_id, msg.content, msg_id)        print(f"发送出去的消息:{msg.content}")# ===== 5. WebSocket 主循环 =====async def main():    async with aiohttp.ClientSession() as session:        token = await get_access_token(session)        print(f"Token 获取成功")        gateway_url = await get_gateway_url(session, token)        print(f"Gateway: {gateway_url}")        async with session.ws_connect(gateway_url) as ws:            heartbeat_interval = None            async for msg in ws:                data = json.loads(msg.data)                op = data.get("op")                t  = data.get("t")                d  = data.get("d", {})                # OP=10: 服务器发来 Hello,开始心跳                if op == 10:                    heartbeat_interval = d["heartbeat_interval"] / 1000                    asyncio.create_task(heartbeat(ws, heartbeat_interval))                    # 发送 Identify                    await ws.send_json({                        "op"2,                        "d": {                            "token"f"QQBot {token}",                            "intents"1107300352,  # 群聊+私信+频道,和 OpenClaw 一样                            "shard": [01],                        }                    })                # OP=0: 事件派发                elif op == 0:                    if t == "C2C_MESSAGE_CREATE":       # 私聊消息                        await handle_message(session, token, d)                    elif t == "GROUP_AT_MESSAGE_CREATE"# 群@消息                        print(f"群消息: {d}")                    elif t == "READY":                        print(f"Bot 已就绪: {d['user']['username']}")                # OP=11: 心跳 ACK                elif op == 11:                    print("心跳 ACK")# ===== 心跳 =====async def heartbeat(ws, interval):    while True:        await asyncio.sleep(interval)        await ws.send_json({"op"1"d"None})        print("发送心跳")asyncio.run(main())
这样的话就可以通过工具,来操控电脑,比如打印当前目录的文件。打开CMD等等。
以上两百多行代码,简单的openClaw实现过程。
python解释器
git clone https://github.com/python/cpython.gitcd cpythoncd cpythonsudo apt updatesudo apt install -y build-essential libssl-dev zlib1g-dev libbz2-dev \    libreadline-dev libsqlite3-dev libffi-dev libncurses5-dev libgdbm-dev \    liblzma-dev ./configure --prefix=$HOME/opt/python-debug --with-pydebug make -j$(nproc) make install
hello.py
x = 1 + 2print(x)
debug
gdb $HOME/opt/python-debug/bin/python3 set args hello.pyb _PyAssemble_MakeCodeObjectb assemble_emitrcfinish //运行完成assemble_emit函数,因下面要查看其字节码 (gdb)x/32xb ((PyBytesObject*)a.a_bytecode)->ob_sval0x7ffff6cde8800x80    0x00    0x00    0x00    0x5d    0x03    0x73    0x000x7ffff6cde8880x5c    0x01    0x1f    0x00    0x5c    0x00    0x32    0x010x7ffff6cde8900x00    0x00    0x00    0x00    0x00    0x00    0x1d    0x000x7ffff6cde8980x51    0x01    0x21    0x00    0x00    0xfd    0xfd    0xfd
我们可以模拟还原字节码
import diswith open("hello.py"as f:    src = f.read()code = compile(src, "hello.py""exec")print(code.co_names)dis.dis(code)import opcode# 查单个 opcode 的名称print(opcode.opname[128])   # → RESUMEprint(opcode.opname[93])    # → FOR_ITERprint(opcode.opname[92])    # → UNPACK_SEQUENCE 或其他# 批量解析你的字节raw = [0x80,0x000x00,0x000x5d,0x030x73,0x00,       0x5c,0x010x1f,0x000x5c,0x000x32,0x01]for i in range(0len(raw), 2):    op  = raw[i]    arg = raw[i+1]    print(f"  offset={i:3d}  opcode={op:3d} ({opcode.opname[op]:30s})  arg={arg}")
结果
E:\python14\python.exe D:\PyCharm\PythonProject7\asm.py ('x''print')  0           RESUME                   0  1           LOAD_SMALL_INT           3              STORE_NAME               0 (x)  2           LOAD_NAME                1 (print)              PUSH_NULL              LOAD_NAME                0 (x)              CALL                     1              POP_TOP              LOAD_CONST               1 (None)              RETURN_VALUERESUMELOAD_NAMELOAD_GLOBAL  offset=  0  opcode=128 (RESUME                        )  arg=0  offset=  2  opcode=  0 (CACHE                         )  arg=0  offset=  4  opcode= 93 (LOAD_NAME                     )  arg=3  offset=  6  opcode=115 (STORE_GLOBAL                  )  arg=0  offset=  8  opcode= 92 (LOAD_GLOBAL                   )  arg=1  offset= 10  opcode= 31 (POP_TOP                       )  arg=0  offset= 12  opcode= 92 (LOAD_GLOBAL                   )  arg=0  offset= 14  opcode= 50 (BUILD_STRING                  )  arg=1进程已结束,退出代码为 0
offset  字节           opcode名称              arg   说明─────────────────────────────────────────────────────────────  0    0x80 0x00      RESUME                  0     函数入口检查点  2    0x00 0x00      CACHE                   0     内联缓存占位  4    0x5d 0x03      LOAD_SMALL_INT          3     把整数 3 压栈  ← 1+2已在编译期算好  6    0x73 0x00      STORE_NAME              0     弹栈存入 x  8    0x5c 0x01      LOAD_NAME               1     压栈 print 10    0x1f 0x00      PUSH_NULL               0     压入 NULL(调用约定) 12    0x5c 0x00      LOAD_NAME               0     压栈 x 14    0x32 0x01      CALL                    1     调用,参数个数=1  …    0x00 ...       CACHE×4                       CALL 后跟4个缓存槽       0x1d 0x00      POP_TOP                 0     丢弃 print() 返回值       0x51 0x01      LOAD_CONST              1     压栈 None       0x21 0x00      RETURN_VALUE            0     返回
细节
x = 1 + 2```**GDB 里看不到加法指令**,因为编译器在 `_PyCfg_OptimizeCodeUnit` 阶段做了**常量折叠**:```优化前:                        优化后:  LOAD_SMALL_INT  1               LOAD_SMALL_INT  3   ← 直接是3  LOAD_SMALL_INT  2               STORE_NAME      0  BINARY_OP       +  STORE_NAME      0
何时编译成机器码,解释器不会编译机器码,只会进行解释执行。JIT则会,新版的python可以,这里不介绍。
解释知悉如下:
//home/tang/opt/cpython/cpython/Python/generated_cases.c.h 比如把整数3压入栈TARGET(LOAD_SMALL_INT) {            #if _Py_TAIL_CALL_INTERP            int opcode = LOAD_SMALL_INT;            (void)(opcode);            #endif            frame->instr_ptr = next_instr;            next_instr += 1;            INSTRUCTION_STATS(LOAD_SMALL_INT);            _PyStackRef value;            assert(oparg < _PY_NSMALLPOSINTS);            PyObject *obj = (PyObject *)&_PyLong_SMALL_INTS[_PY_NSMALLNEGINTS + oparg];            value = PyStackRef_FromPyObjectBorrow(obj);            stack_pointer[0] = value;            stack_pointer += 1;            ASSERT_WITHIN_STACK_BOUNDS(__FILE____LINE__);            DISPATCH();        }
dispatch则继续下一个指令执行执行,以完成当前函数。
OSR堆栈替换(net10)
前言
osr堆栈替换大约前面几个版本就引进了,这里依旧看下新版(net10)的实现。
详情
benchmark官例
using BenchmarkDotNet.Attributes;using BenchmarkDotNet.Running;using System;[DisassemblyDiagnoser]publicpartialclassProgram{staticvoidMain(string[] args) => BenchmarkSwitcher.FromAssembly(typeof(Program).Assembly).Run(args);privateint _a = 42, _b = 84;    [Benchmark]publicintMin() => Math.Min(_a, _b);}
  运行
// 注意这里 releas 如果是debug则测不出来,如果要asm,则 $env:DOTNET_JitDisasm="Main"$env:DOTNET_JitDump="Main"$env:DOTNET_JitDisasm="Main"dotnet run -c release -f net9.0--filter'Program:*'
我们如果通过clr debug也需要release版本的托管dll,且设置以下环境变量
CORE_LIBRARIES=C:\Program Files\dotnet\shared\Microsoft.NETCore.App\10.0.0-preview.7.25380.108DOTNET_TieredCompilation=1DOTNET_TieredPGO=1DOTNET_ReadyToRun=1DOTNET_JitDump=MainDOTNET_TC_QuickJitForLoops=1DOTNET_EnableWriteXorExecute=0
Linux下面,需要设置环境变量
vim ~/.bashrcexport CORE_LIBRARIES=/usr/lib/dotnet/shared/Microsoft.NETCore.App/10.0.4/export LD_LIBRARY_PATH=/home/tang/opt/dotnet/runtime/artifacts/bin/coreclr/linux.x64.Debug:$LD_LIBRARY_PATH
以安装dotnet-sos全局,再install(不要忘记install,否则lldb不识别)
dotnet tool install --global dotnet-sosdotnet-sos install
install之后lldb才算成功
vim ~/.lldbinit#START - ADDED BY SOS INSTALLERplugin load /home/tang/.dotnet/sos/libsosplugin.sosetsymbolserver -ms#END - ADDED BY SOS INSTALLER
以下例子为说明测试,注意放入到coreclr\windows.x64.Debug路径必须是托管dll的release才能看到结果
staticvoidMain() {var sw = new System.Diagnostics.Stopwatch();while (true)     {         sw.Restart();for (int trial = 0; trial < 10_000; trial++)         {int count = 0;for (int i = 0; i < char.MaxValue; i++)if (IsAsciiDigit((char)i))                     count++;         }         sw.Stop();         Console.WriteLine(sw.Elapsed);     }staticboolIsAsciiDigit(char c) => (uint)(c - '0') <= 9; }
在clr导入IL的时候,会判断当前这个il函数是否适合启用osr。
先看jit日志,导入到txt里面是便于搜索观看,比如关键字:osr,patch等。
$env:CORE_LIBRARIES="C:\Program Files\dotnet\shared\Microsoft.NETCore.App\10.0.0-preview.7.25380.108"$env:DOTNET_JitDump="Main".\corerun.exe .\测试.dll > jitlog.txt
osr key jit日志,就表示当前方法适合使用osr。
OSR enabled forthis method
判断其是否适合使用方法,这里大约是,如果方法里有:方法里有local(在栈上分配动态大小内存),表示异常处理块里有向前跳转(OSR 不支持在 handler 内部循环 → 不安全 → 不能生成 patchpoint),托管代码被 native 调用(OSR 不能在 reverse P/Invoke 情况下插入 patchpoint → 因为栈布局可能不符合预期),方法是实例方法,并且 this 可修改(OSR 要求 this 是只读或者静态方法,确保栈上数据一致 → 如果可修改就不安全),以及如果编译时没有开启 FEATURE_ON_STACK_REPLACEMENT,直接说明 OSR 功能不可用。
// inlineboolCompiler::compCanHavePatchpoints(constchar** reason){constchar* whyNot = nullptr;#ifdef FEATURE_ON_STACK_REPLACEMENTif (compLocallocSeen)    {        whyNot = "OSR can't handle localloc";    }elseif (compHasBackwardJumpInHandler)    {        whyNot = "OSR can't handle loop in handler";    }elseif (opts.IsReversePInvoke())    {        whyNot = "OSR can't handle reverse pinvoke";    }elseif (!info.compIsStatic && !lvaIsOriginalThisReadOnly())    {        whyNot = "OSR can't handle modifiable this";    }#else    whyNot = "OSR feature not defined in build";#endifif (reason != nullptr)    {        *reason = whyNot;    }return whyNot == nullptr;}
这是第一步。
因JIT编译的时候把IL分成一个个BB块,JIT会判断osr放到哪个块合适。比如
Can't set source patchpoint at BB10, using target BB02 instead
代码
if (succBlock->bbStackDepthOnEntry() >0){JITDUMP("\nCan't set source patchpoint at "FMT_BB", can't use target "FMT_BB" as it has non-empty stack on entry.\n",            block->bbNum, succBlock->bbNum);}else{JITDUMP("\nCan't set source patchpoint at "FMT_BB", using target "FMT_BB" instead\n",            block->bbNum, succBlock->bbNum);assert(!succBlock->hasHndIndex());    succBlock->SetFlags(BBF_PATCHPOINT);}
下面就是把osr插入到可以放入的bb块
*************** Starting PHASE Expand patchpointsPatchpoint: regular patchpoint in BB02
日志,这里主要看TransformBlock
// patchpoint.cppif (block->HasFlag(BBF_PATCHPOINT))    {// We can't OSR from funclets.//assert(!block->hasHndIndex());// Clear the patchpoint flag.//        block->RemoveFlags(BBF_PATCHPOINT);JITDUMP("Patchpoint: regular patchpoint in "FMT_BB"\n", block->bbNum);TransformBlock(block);        count++;    }
代码,注意看这里
    void TransformBlock(BasicBlock* block)    {        // If we haven't allocated the counter temp yet, set it up        if (ppCounterLclNum == BAD_VAR_NUM)        {            ppCounterLclNum                            = compiler->lvaGrabTemp(true DEBUGARG("patchpoint counter"));            compiler->lvaTable[ppCounterLclNum].lvType = TYP_INT;            // and initialize in the entry block            TransformEntry(compiler->fgFirstBB);        }        // Capture the IL offset        IL_OFFSET ilOffset = block->bbCodeOffs;        assert(ilOffset != BAD_IL_OFFSET);        // Current block now becomes the test block        // 把这个block切成两个bloc块,bloc块和remainderBlock块        BasicBlock* remainderBlock = compiler->fgSplitBlockAtBeginning(block);        // 创建一个helperBlock块           BasicBlock* helperBlock    = CreateAndInsertBasicBlock(BBJ_ALWAYS, block);        // Update flow and flags        block->SetFlags(BBF_INTERNAL);        helperBlock->SetFlags(BBF_BACKWARD_JUMP);        //这个断言确保block 块的目标是remainderblock 块,这里目标target         assert(block->TargetIs(remainderBlock));        // helperblock 块的前置块是block 块         // edge表示某条支线或者边, 比如下面falseedge表示 block到helperblock的支线         FlowEdge* const falseEdge = compiler->fgAddRefPred(helperBlock, block);        // 获取block 块的目标边 也即是 remainderblock,上面断言同样         FlowEdge* const trueEdge  = block->GetTargetEdge();        // 设置这个边的概率,也就是分支预测执行概率 这里大数值,也即是执行比较多。下面的falseEdge小数值 也就是执行比较少         trueEdge->setLikelihood(HIGH_PROBABILITY / 100.0);        falseEdge->setLikelihood((100 - HIGH_PROBABILITY) / 100.0);        block->SetCond(trueEdge, falseEdge);        // 设置remainderBlock 块的前置块是helperblock        FlowEdge* const newEdge = compiler->fgAddRefPred(remainderBlock, helperBlock);        //再设置helperblock的目标是remainderblock,这样 remainderblock就有两条支线到达了          helperBlock->SetTargetEdge(newEdge);        // Update weights        remainderBlock->inheritWeight(block);        helperBlock->inheritWeightPercentage(block, 100 - HIGH_PROBABILITY);        // Fill in test block        //        // --ppCounter;        GenTree* ppCounterBefore = compiler->gtNewLclvNode(ppCounterLclNum, TYP_INT);        GenTree* one             = compiler->gtNewIconNode(1, TYP_INT);        // 表示ppCounterBefore-one  ppCounterSub 表示一个节点         GenTree* ppCounterSub    = compiler->gtNewOperNode(GT_SUB, TYP_INT, ppCounterBefore, one);        // 表示把 上面ppCounterSub的结果写回ppCounterLclNum        GenTree* ppCounterUpdate = compiler->gtNewStoreLclVarNode(ppCounterLclNum, ppCounterSub);        // 把这棵树插入到block块的末尾。,下面所有都是以此类推         compiler->fgNewStmtAtEnd(block, ppCounterUpdate);        // if (ppCounter > 0), bypass helper call        GenTree* ppCounterUpdated = compiler->gtNewLclvNode(ppCounterLclNum, TYP_INT);        GenTree* zero             = compiler->gtNewIconNode(0, TYP_INT);        GenTree* compare          = compiler->gtNewOperNode(GT_GT, TYP_INT, ppCounterUpdated, zero);        GenTree* jmp              = compiler->gtNewOperNode(GT_JTRUE, TYP_VOID, compare);        compiler->fgNewStmtAtEnd(block, jmp);        // Fill in helper block        //        // call PPHelper(&ppCounter, ilOffset)        GenTree*     ilOffsetNode  = compiler->gtNewIconNode(ilOffset, TYP_INT);        GenTree*     ppCounterAddr = compiler->gtNewLclVarAddrNode(ppCounterLclNum);        GenTreeCall* helperCall =            compiler->gtNewHelperCallNode(CORINFO_HELP_PATCHPOINT, TYP_VOID, ppCounterAddr, ilOffsetNode);        compiler->fgNewStmtAtEnd(helperBlock, helperCall);    }
这段代码主要是构建了以下三行IL
--ppCounter;if (ppCounter > 0), bypass helper call call PPHelper(&ppCounter, ilOffset) 代码原理 : 1.先分割block为两个block(block和remainderblock): fgSplitBlockAtBeginning   注意这里的分割之后,block的目标(target)依旧是remainderblock,这点可以通过断言看出来assert(block->TargetIs(remainderBlock)) 2.新建一个block(helperblock):CreateAndInsertBasicBlock 3.把helperblock插入到block和remainderblock的中间,进行了分支预测。trueedge和falsedge 4.像这种:gtNewLclvNode表示新建一个本地(Lcl)变量(v)节点(Node)   gtNewIconNode新建一个常量(Icon)节点(Node)
那么代码块插入了之后,后面会进行一个调整,最终插入到bb12块
***** BB12 [0011]STMT00024 ( ??? ... ??? )               [000075] --CXG------                         *  CALL help void   CORINFO_HELP_PATCHPOINT               [000074] ----------- arg0                    +--*  LCL_ADDR  long   V07 tmp3         [+0]               [000073] ----------- arg1                    \--*  CNS_INT   int6------------ BB11 [0010] [006..010) -> BB09(1) (always), preds={BB02,BB12} succs={BB09}
此时就完成了,类似于这种的asm
G_M000_IG04:                ;; offset=0x0059lea      rcx, [rbp-0x68]mov      edx, 6call     CORINFO_HELP_PATCHPOINT
OK,以上是JIT布局生成的BB块以及asm。
这种布局完成之后何时运行呢?
这里我们可以把托管的DLL编译成release模式,放入到runtime目录调试。
主要做了以下事情:
1.环境变量判断hitLimit,hitCount
2.回溯,
RtlCaptureContext获取当前所在帧栈状态的contextVirtualUnwindToFirstManagedCallFrame找到当前调用堆栈的第一个托管帧栈(这里是main),如果中间有非托管直接跳过CallDescrWorker获取调用当前函数的函数帧栈,也即是main的上一层(CallDescrWorkerInternalReturnAddress)
3.osr取代原有的函数头入口以及执行
// 取代SetIP(pFrameContext, osrMethodCode)// 执行ClrRestoreNonvolatileContext(pFrameContext);
我们先看JIT_Patchpoint
NESTED_ENTRY JIT_Patchpoint, _TEXT        PROLOG_WITH_TRANSITION_BLOCK        lea     rcx, [rsp + __PWTB_TransitionBlock] ; TransitionBlock *        call    JIT_PatchpointWorkerWorkerWithPolicy        EPILOG_WITH_TRANSITION_BLOCK_RETURN        TAILJMP_RAXNESTED_END JIT_Patchpoint, _TEXT
JIT_PatchpointWorkerWorkerWithPolicy,这个函数里面进行了三次回溯。
extern "C" voidJIT_PatchpointWorkerWorkerWithPolicy(TransitionBlock * pTransitionBlock){   osrMethodCode = PatchpointOptimizationPolicy(pTransitionBlock, counter, ilOffset, ppInfo, codeInfo, &isNewMethod);   CONTEXT *pFrameContext = NULL;   success = g_pfnInitializeContext2 ?       g_pfnInitializeContext2(pBuffer, contextFlags, &pFrameContext, &contextSize, xStateCompactionMask) :       InitializeContext(pBuffer, contextFlags, &pFrameContext, &contextSize);   //获取调用当前函数的快照   RtlCaptureContext(pFrameContext);   // 回溯到当前堆栈第一次托管函数调用的帧栈,也即是main   pThread->VirtualUnwindToFirstManagedCallFrame(pFrameContext);   // 向上回溯一次,来到了clr调用Main函数的地方,也即是CallDescrWorkerInternalReturnAddress   // 所以此时的rip是CallDescrWorkerInternalReturnAddress函数头所在的地址    RtlVirtualUnwind(UNW_FLAG_NHANDLER, callerCodeInfo.GetModuleBase(), GetIP(pFrameContext), callerCodeInfo.GetFunctionEntry(),       pFrameContext, &handlerData, &establisherFrame, NULL);           // 设置优化后的代码,也即是osr的函数头地址    SetIP(pFrameContext, osrMethodCode);    // 设置好了函数头,下面就调用函数头    ClrRestoreNonvolatileContext(pFrameContext);}
注意上面的PatchpointOptimizationPolicy函数,它里面有些环境变量
// 这个是设置程序循环或者运行了多少次才会触发jit_patchpoint调用 默认是0x3e8也即是1000次 const int counterBump = g_pConfig->OSR_CounterBump();// 如果jit_patchpoint调用没有10次(hitLimit),则没有osr优化后的机器码// hitLimit也是环境变量, 默认10// hitCount表示每调用一次jit_patchpoint就会加1 ,当等于10 则会有优化的OSR代码 // 否则直接返回,也即是goto DONE这个代码 const int hitLimit = g_pConfig->OSR_HitLimit();const int hitCount = InterlockedIncrement(&ppInfo->m_patchpointCount);const int hitLogLevel = (hitCount == 1) ? LL_INFO10 : LL_INFO1000;if (hitCount < hitLimit){    goto DONE;}后面的调用 JitPatchpointWorker-》pMD->PrepareCode开启了新的JIT编译返回的结果即是osrMehtodCode。
下是clr里面获取osr的DebugInfo信息,用于运行时获取信息用。
voidCompiler::generatePatchpointInfo(){if (!doesMethodHavePatchpoints() && !doesMethodHavePartialCompilationPatchpoints())    {// Nothing to reportreturn;    }// Patchpoints are only found in Tier0 code, which is unoptimized, and so// should always have frame pointer.assert(codeGen->isFramePointerUsed());// Allocate patchpoint info storage from runtime, and fill in initial bits of data.constunsigned        patchpointInfoSize = PatchpointInfo::ComputeSize(info.compLocalsCount);    PatchpointInfo* const patchpointInfo     = (PatchpointInfo*)info.compCompHnd->allocateArray(patchpointInfoSize);// Patchpoint offsets always refer to "virtual frame offsets".//// For x64 this falls out because Tier0 frames are always FP frames, and so the FP-relative// offset is what we want.//// For arm64, if the frame pointer is not at the top of the frame, we need to adjust the// offset.#if defined(TARGET_AMD64)// We add +TARGET_POINTER_SIZE here is to account for the slot that Jit_Patchpoint// creates when it simulates calling the OSR method (the "pseudo return address" slot).// This is effectively a new slot at the bottom of the Tier0 frame.//constint totalFrameSize = codeGen->genTotalFrameSize() + TARGET_POINTER_SIZE;constint offsetAdjust   = 0;#elif defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64)// SP is not manipulated by calls so no frame size adjustment needed.// Local Offsets may need adjusting, if FP is at bottom of frame.//constint totalFrameSize = codeGen->genTotalFrameSize();constint offsetAdjust   = codeGen->genSPtoFPdelta() - totalFrameSize;#elseNYI("patchpoint info generation");constint offsetAdjust   = 0;constint totalFrameSize = 0;#endif    patchpointInfo->Initialize(info.compLocalsCount, totalFrameSize);JITDUMP("--OSR--- Total Frame Size %d, local offset adjust is %d\n", patchpointInfo->TotalFrameSize(),            offsetAdjust);// We record offsets for all the "locals" here. Could restrict// this to just the IL locals with some extra logic, and save a bit of space,// but would need to adjust all consumers, too.for (unsigned lclNum = 0; lclNum < info.compLocalsCount; lclNum++)    {// If there are shadowed params, the patchpoint info should refer to the shadow copy.//unsigned varNum = lclNum;if (gsShadowVarInfo != nullptr)        {unsignedconst shadowNum = gsShadowVarInfo[lclNum].shadowCopy;if (shadowNum != BAD_VAR_NUM)            {assert(shadowNum < lvaCount);assert(shadowNum >= info.compLocalsCount);                varNum = shadowNum;            }        }        LclVarDsc* const varDsc = lvaGetDesc(varNum);// We expect all these to have stack homes, and be FP relativeassert(varDsc->lvOnFrame);assert(varDsc->lvFramePointerBased);// Record FramePtr relative offset (no localloc yet)// Note if IL stream contained an address-of that potentially leads to exposure.// That bit of IL might be skipped by OSR partial importation.constbool isExposed = varDsc->lvHasLdAddrOp;        patchpointInfo->SetOffsetAndExposure(lclNum, varDsc->GetStackOffset() + offsetAdjust, isExposed);JITDUMP("--OSR-- V%02u is at virtual offset %d%s%s\n", lclNum, patchpointInfo->Offset(lclNum),                patchpointInfo->IsExposed(lclNum) ? " (exposed)" : "", (varNum != lclNum) ? " (shadowed)" : "");    }// Special offsets//if (lvaReportParamTypeArg())    {constint offset = lvaCachedGenericContextArgOffset();        patchpointInfo->SetGenericContextArgOffset(offset + offsetAdjust);JITDUMP("--OSR-- cached generic context virtual offset is %d\n", patchpointInfo->GenericContextArgOffset());    }if (lvaKeepAliveAndReportThis())    {constint offset = lvaCachedGenericContextArgOffset();        patchpointInfo->SetKeptAliveThisOffset(offset + offsetAdjust);JITDUMP("--OSR-- kept-alive this virtual offset is %d\n", patchpointInfo->KeptAliveThisOffset());    }if (compGSReorderStackLayout)    {assert(lvaGSSecurityCookie != BAD_VAR_NUM);        LclVarDsc* const varDsc = lvaGetDesc(lvaGSSecurityCookie);        patchpointInfo->SetSecurityCookieOffset(varDsc->GetStackOffset() + offsetAdjust);JITDUMP("--OSR-- security cookie V%02u virtual offset is %d\n", lvaGSSecurityCookie,                patchpointInfo->SecurityCookieOffset());    }if (lvaMonAcquired != BAD_VAR_NUM)    {        LclVarDsc* const varDsc = lvaGetDesc(lvaMonAcquired);        patchpointInfo->SetMonitorAcquiredOffset(varDsc->GetStackOffset() + offsetAdjust);JITDUMP("--OSR-- monitor acquired V%02u virtual offset is %d\n", lvaMonAcquired,                patchpointInfo->MonitorAcquiredOffset());    }#if defined(TARGET_AMD64)// Record callee save registers.// Currently only needed for x64.//    regMaskTP rsPushRegs = codeGen->regSet.rsGetModifiedCalleeSavedRegsMask();    rsPushRegs |= RBM_FPBASE;    patchpointInfo->SetCalleeSaveRegisters((uint64_t)rsPushRegs);JITDUMP("--OSR-- Tier0 callee saves: ");JITDUMPEXEC(dspRegMask((regMaskTP)patchpointInfo->CalleeSaveRegisters()));JITDUMP("\n");#endif// Register this with the runtime.    info.compCompHnd->setPatchpointInfo(patchpointInfo);}
以上就是OSR的整个过程。
杂项
cloudflare自定义域名
wokers&pages->你的网站名称->settings->Domains & Routes->add
dns规则
domains->overview->域名->dns->records->可以看到其规则情况
影子堆栈
攻击目标:┌─────────────────┐│   返回地址       │  ← 覆盖这里 → 劫持程序执行流├─────────────────┤│   参数/变量      │  ← 覆盖这里 → 篡改程序逻辑└─────────────────┘Canary  → 防止返回地址被覆盖Shadow  → 防止重要参数/变量被覆盖GS Cookie = Canary = Security CookieShadow是GS编译的一部分高地址┌──────────────────┐│  param_shadow    │  ← Shadow Copy 保护这里├──────────────────┤    编译器只用shadow,original被覆盖没关系│  saved RBP       │├──────────────────┤│  返回地址         │  ← Canary 保护这里├──────────────────┤│  Canary          │  ← 哨兵,溢出必须先经过它├──────────────────┤│  param_original  │  ← 牺牲品,被覆盖也无所谓├──────────────────┤│  buffer[16]      ││  buffer[0]       │  ← 溢出起点└──────────────────┘低地址
结尾
本篇三部分,分别是openclaw/cpython/os源码级别分析
基本 文件 流程 错误 SQL 调试
  1. 请求信息 : 2026-04-03 13:20:33 HTTP/1.1 GET : https://www.yeyulingfeng.com/a/496620.html
  2. 运行时间 : 0.144898s [ 吞吐率:6.90req/s ] 内存消耗:5,353.21kb 文件加载:145
  3. 缓存信息 : 0 reads,0 writes
  4. 会话信息 : SESSION_ID=b6f6b4e3617195f62d61262b41d2c2e8
  1. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/public/index.php ( 0.79 KB )
  2. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/autoload.php ( 0.17 KB )
  3. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/composer/autoload_real.php ( 2.49 KB )
  4. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/composer/platform_check.php ( 0.90 KB )
  5. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/composer/ClassLoader.php ( 14.03 KB )
  6. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/composer/autoload_static.php ( 6.05 KB )
  7. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-helper/src/helper.php ( 8.34 KB )
  8. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-validate/src/helper.php ( 2.19 KB )
  9. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/ralouphie/getallheaders/src/getallheaders.php ( 1.60 KB )
  10. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/helper.php ( 1.47 KB )
  11. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/stubs/load_stubs.php ( 0.16 KB )
  12. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Exception.php ( 1.69 KB )
  13. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-container/src/Facade.php ( 2.71 KB )
  14. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/symfony/deprecation-contracts/function.php ( 0.99 KB )
  15. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/symfony/polyfill-mbstring/bootstrap.php ( 8.26 KB )
  16. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/symfony/polyfill-mbstring/bootstrap80.php ( 9.78 KB )
  17. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/symfony/var-dumper/Resources/functions/dump.php ( 1.49 KB )
  18. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-dumper/src/helper.php ( 0.18 KB )
  19. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/symfony/var-dumper/VarDumper.php ( 4.30 KB )
  20. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/guzzlehttp/guzzle/src/functions_include.php ( 0.16 KB )
  21. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/guzzlehttp/guzzle/src/functions.php ( 5.54 KB )
  22. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/App.php ( 15.30 KB )
  23. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-container/src/Container.php ( 15.76 KB )
  24. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/psr/container/src/ContainerInterface.php ( 1.02 KB )
  25. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/app/provider.php ( 0.19 KB )
  26. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Http.php ( 6.04 KB )
  27. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-helper/src/helper/Str.php ( 7.29 KB )
  28. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Env.php ( 4.68 KB )
  29. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/app/common.php ( 0.03 KB )
  30. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/helper.php ( 18.78 KB )
  31. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Config.php ( 5.54 KB )
  32. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/alipay.php ( 3.59 KB )
  33. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/facade/Env.php ( 1.67 KB )
  34. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/app.php ( 0.95 KB )
  35. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/cache.php ( 0.78 KB )
  36. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/console.php ( 0.23 KB )
  37. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/cookie.php ( 0.56 KB )
  38. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/database.php ( 2.48 KB )
  39. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/filesystem.php ( 0.61 KB )
  40. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/lang.php ( 0.91 KB )
  41. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/log.php ( 1.35 KB )
  42. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/middleware.php ( 0.19 KB )
  43. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/route.php ( 1.89 KB )
  44. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/session.php ( 0.57 KB )
  45. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/trace.php ( 0.34 KB )
  46. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/view.php ( 0.82 KB )
  47. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/app/event.php ( 0.25 KB )
  48. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Event.php ( 7.67 KB )
  49. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/app/service.php ( 0.13 KB )
  50. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/app/AppService.php ( 0.26 KB )
  51. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Service.php ( 1.64 KB )
  52. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Lang.php ( 7.35 KB )
  53. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/lang/zh-cn.php ( 13.70 KB )
  54. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/initializer/Error.php ( 3.31 KB )
  55. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/initializer/RegisterService.php ( 1.33 KB )
  56. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/services.php ( 0.14 KB )
  57. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/service/PaginatorService.php ( 1.52 KB )
  58. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/service/ValidateService.php ( 0.99 KB )
  59. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/service/ModelService.php ( 2.04 KB )
  60. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-trace/src/Service.php ( 0.77 KB )
  61. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Middleware.php ( 6.72 KB )
  62. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/initializer/BootService.php ( 0.77 KB )
  63. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/Paginator.php ( 11.86 KB )
  64. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-validate/src/Validate.php ( 63.20 KB )
  65. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/Model.php ( 23.55 KB )
  66. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/model/concern/Attribute.php ( 21.05 KB )
  67. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/model/concern/AutoWriteData.php ( 4.21 KB )
  68. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/model/concern/Conversion.php ( 6.44 KB )
  69. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/model/concern/DbConnect.php ( 5.16 KB )
  70. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/model/concern/ModelEvent.php ( 2.33 KB )
  71. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/model/concern/RelationShip.php ( 28.29 KB )
  72. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-helper/src/contract/Arrayable.php ( 0.09 KB )
  73. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-helper/src/contract/Jsonable.php ( 0.13 KB )
  74. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/model/contract/Modelable.php ( 0.09 KB )
  75. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Db.php ( 2.88 KB )
  76. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/DbManager.php ( 8.52 KB )
  77. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Log.php ( 6.28 KB )
  78. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Manager.php ( 3.92 KB )
  79. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/psr/log/src/LoggerTrait.php ( 2.69 KB )
  80. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/psr/log/src/LoggerInterface.php ( 2.71 KB )
  81. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Cache.php ( 4.92 KB )
  82. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/psr/simple-cache/src/CacheInterface.php ( 4.71 KB )
  83. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-helper/src/helper/Arr.php ( 16.63 KB )
  84. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/cache/driver/File.php ( 7.84 KB )
  85. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/cache/Driver.php ( 9.03 KB )
  86. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/contract/CacheHandlerInterface.php ( 1.99 KB )
  87. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/app/Request.php ( 0.09 KB )
  88. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Request.php ( 55.78 KB )
  89. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/app/middleware.php ( 0.25 KB )
  90. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Pipeline.php ( 2.61 KB )
  91. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-trace/src/TraceDebug.php ( 3.40 KB )
  92. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/middleware/SessionInit.php ( 1.94 KB )
  93. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Session.php ( 1.80 KB )
  94. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/session/driver/File.php ( 6.27 KB )
  95. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/contract/SessionHandlerInterface.php ( 0.87 KB )
  96. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/session/Store.php ( 7.12 KB )
  97. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Route.php ( 23.73 KB )
  98. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/route/RuleName.php ( 5.75 KB )
  99. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/route/Domain.php ( 2.53 KB )
  100. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/route/RuleGroup.php ( 22.43 KB )
  101. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/route/Rule.php ( 26.95 KB )
  102. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/route/RuleItem.php ( 9.78 KB )
  103. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/route/app.php ( 3.94 KB )
  104. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/facade/Route.php ( 4.70 KB )
  105. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/route/dispatch/Controller.php ( 4.74 KB )
  106. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/route/Dispatch.php ( 10.44 KB )
  107. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/app/controller/Index.php ( 9.87 KB )
  108. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/app/BaseController.php ( 2.05 KB )
  109. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/facade/Db.php ( 0.93 KB )
  110. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/connector/Mysql.php ( 5.44 KB )
  111. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/PDOConnection.php ( 52.47 KB )
  112. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/Connection.php ( 8.39 KB )
  113. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/ConnectionInterface.php ( 4.57 KB )
  114. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/builder/Mysql.php ( 16.58 KB )
  115. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/Builder.php ( 24.06 KB )
  116. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/BaseBuilder.php ( 27.50 KB )
  117. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/Query.php ( 15.71 KB )
  118. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/BaseQuery.php ( 45.13 KB )
  119. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/concern/TimeFieldQuery.php ( 7.43 KB )
  120. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/concern/AggregateQuery.php ( 3.26 KB )
  121. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/concern/ModelRelationQuery.php ( 20.07 KB )
  122. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/concern/ParamsBind.php ( 3.66 KB )
  123. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/concern/ResultOperation.php ( 7.01 KB )
  124. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/concern/WhereQuery.php ( 19.37 KB )
  125. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/concern/JoinAndViewQuery.php ( 7.11 KB )
  126. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/concern/TableFieldInfo.php ( 2.63 KB )
  127. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/concern/Transaction.php ( 2.77 KB )
  128. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/log/driver/File.php ( 5.96 KB )
  129. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/contract/LogHandlerInterface.php ( 0.86 KB )
  130. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/log/Channel.php ( 3.89 KB )
  131. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/event/LogRecord.php ( 1.02 KB )
  132. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-helper/src/Collection.php ( 16.47 KB )
  133. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/facade/View.php ( 1.70 KB )
  134. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/View.php ( 4.39 KB )
  135. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/app/controller/Es.php ( 3.30 KB )
  136. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Response.php ( 8.81 KB )
  137. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/response/View.php ( 3.29 KB )
  138. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Cookie.php ( 6.06 KB )
  139. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-view/src/Think.php ( 8.38 KB )
  140. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/contract/TemplateHandlerInterface.php ( 1.60 KB )
  141. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-template/src/Template.php ( 46.61 KB )
  142. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-template/src/template/driver/File.php ( 2.41 KB )
  143. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-template/src/template/contract/DriverInterface.php ( 0.86 KB )
  144. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/runtime/temp/c935550e3e8a3a4c27dd94e439343fdf.php ( 31.80 KB )
  145. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-trace/src/Html.php ( 4.42 KB )
  1. CONNECT:[ UseTime:0.000534s ] mysql:host=127.0.0.1;port=3306;dbname=wenku;charset=utf8mb4
  2. SHOW FULL COLUMNS FROM `fenlei` [ RunTime:0.000801s ]
  3. SELECT * FROM `fenlei` WHERE `fid` = 0 [ RunTime:0.000298s ]
  4. SELECT * FROM `fenlei` WHERE `fid` = 63 [ RunTime:0.000312s ]
  5. SHOW FULL COLUMNS FROM `set` [ RunTime:0.000760s ]
  6. SELECT * FROM `set` [ RunTime:0.000217s ]
  7. SHOW FULL COLUMNS FROM `article` [ RunTime:0.000835s ]
  8. SELECT * FROM `article` WHERE `id` = 496620 LIMIT 1 [ RunTime:0.000760s ]
  9. UPDATE `article` SET `lasttime` = 1775193633 WHERE `id` = 496620 [ RunTime:0.007738s ]
  10. SELECT * FROM `fenlei` WHERE `id` = 64 LIMIT 1 [ RunTime:0.000272s ]
  11. SELECT * FROM `article` WHERE `id` < 496620 ORDER BY `id` DESC LIMIT 1 [ RunTime:0.000681s ]
  12. SELECT * FROM `article` WHERE `id` > 496620 ORDER BY `id` ASC LIMIT 1 [ RunTime:0.000439s ]
  13. SELECT * FROM `article` WHERE `id` < 496620 ORDER BY `id` DESC LIMIT 10 [ RunTime:0.001735s ]
  14. SELECT * FROM `article` WHERE `id` < 496620 ORDER BY `id` DESC LIMIT 10,10 [ RunTime:0.001251s ]
  15. SELECT * FROM `article` WHERE `id` < 496620 ORDER BY `id` DESC LIMIT 20,10 [ RunTime:0.002417s ]
0.146586s