
1. MG-SOFT 是什么它和 MIB 文件的关系不是“导入”而是“编译驱动”很多人第一次看到“MG-SOFT 导入MIB文件”这个说法第一反应是点开软件拖一个 .mib 文件进去弹个成功提示框完事。我当年也是这么想的——直到在客户现场连续三天没让 SNMP Agent 正常上报 trap最后发现根本不是“导入失败”而是压根没理解 MG-SOFT 的底层逻辑。MG-SOFT 不是 MIB 浏览器比如 iReasoning MIB Browser也不是 SNMP 管理站比如 PRTG 或 Zabbix 的 SNMP 插件。它是一个面向嵌入式设备开发的 SNMP 协议栈 SDK 工具链核心组件包括smibdMIB 编译器命令行工具全称SNMP MIB Definition compilermgsoft.h/mgsoft.libC 语言协议栈头文件与静态库mgsoft_agent.exeWindows 下的参考代理演示程序mgsoft_snmpd.conf运行时配置模板关键点来了MG-SOFT 里根本没有“导入”这个操作界面或功能按钮。所谓“导入”实际是指用smibd工具将标准 ASN.1 格式的 MIB 文件如 RFC1213-MIB、IF-MIB、HOST-RESOURCES-MIB编译成 C 结构体定义 OID 映射表 get/set 回调桩代码最终生成一组.h和.c文件供开发者集成进自己的 SNMP Agent 工程中。这就像你不能把.java源码直接“导入”到单片机里运行必须先javac编译成字节码同理MIB 文件是人类可读的协议描述文本而 MG-SOFT 需要的是机器可执行的 C 语言数据结构骨架。不经过smibd编译MIB 就只是文档不是代码。提示很多初学者卡在第一步反复点击 MG-SOFT GUI 界面里的“File → Open”去加载 .mib 文件结果什么反应都没有——因为那个菜单项只支持打开已编译好的.mgcMG-SOFT 自定义中间格式或.conf配置文件不支持原始 ASN.1 MIB 文本。这是设计逻辑差异造成的典型认知错位。为什么必须走编译这一步举个具体例子你在 MIB 中定义了一个标量对象sysUpTime.0类型为TimeTicks。smibd编译后会生成类似这样的 C 代码片段// sysUpTime.c static unsigned long g_sysUpTime_value 0; int mgsoft_get_sysUpTime(int *value) { *value (int)g_sysUpTime_value; return MGSOFT_SUCCESS; } int mgsoft_set_sysUpTime(int value) { // 只读对象拒绝写入 return MGSOFT_ERR_NOTWRITABLE; }同时在 OID 映射表中注册{ {1,3,6,1,2,1,1,3,0}, TYPE_TIMETICKS, mgsoft_get_sysUpTime, mgsoft_set_sysUpTime },没有这一步MG-SOFT 协议栈根本不知道1.3.6.1.2.1.1.3.0对应哪个变量、怎么读、是否允许写。它不像 iReasoning 那样靠运行时解析 MIB 做符号映射——那是管理站做的事MG-SOFT 是被集成到设备固件里的 Agent必须在编译期就固化所有 OID 行为。所以“MG-SOFT 导入MIB文件”这个说法本身就是一个行业误传。准确表述应是使用 smibd 工具对 MIB 文件进行预编译生成可集成的 C 语言 SNMP Agent 代码框架。理解这一点是避开后续所有坑的前提。2. smibd 编译全流程从原始 MIB 到可集成 C 文件的七步实操smibd是整个流程的引擎但它没有图形界面全部依赖命令行参数驱动。官方文档里只有一张参数速查表但没说明每个参数的实际影响边界。我踩过至少 12 次编译失败最终整理出一套稳定、可复现的七步法。以下所有路径、参数、输出结构均基于 MG-SOFT v5.2.1当前主流嵌入式项目所用版本适配 Windows 10/11 和 Linux x86_64通过 WSL2双环境。2.1 准备工作MIB 文件的“洁净度”比语法正确更重要别急着运行smibd。先检查你的 MIB 文件是否真的“干净”。很多从华为、H3C、思科官网下载的 MIB表面看.txt后缀、ASN.1 语法无报错但实际包含三类致命杂质BOM 头Byte Order MarkUTF-8 with BOM 格式会导致smibd解析失败报错Syntax error near line 1。用 VS Code 打开 → 右下角编码显示 → 点击“UTF-8 with BOM” → 选择“Save with Encoding” → 选 “UTF-8” 保存。Windows 换行符CRLF混入 Unix 风格 MIB某些开源 MIB如 NET-SNMP 社区贡献的默认 LF但被 Windows 编辑器二次保存后变成 CRLF。smibd在 Linux 下对此极其敏感。统一转为 LFdos2unix your.mibLinux或用 Notepad → “编辑 → EOL 转换 → UNIX/OSX 格式”。跨 MIB 依赖未显式声明例如你要编译MY-DEVICE-MIB.mib它引用了IF-MIB中的ifIndex但 MIB 文件头部没写IMPORTS ifIndex FROM IF-MIB;。smibd不会自动搜索系统 MIB 库必须手动指定依赖路径。注意MG-SOFT 自带的标准 MIB 库RFC1213-MIB.mib,SNMPv2-SMI.mib等位于安装目录下的mibs\standard\子文件夹。但smibd默认不扫描该路径需显式-I参数引入。2.2 核心命令一条命令搞定基础编译附参数详解假设你的项目目录结构如下project/ ├── mibs/ │ ├── MY-DEVICE-MIB.mib # 主业务 MIB │ └── vendor-specific/ │ └── HUAWEI-UPS-MIB.mib # 华为 UPS 扩展 MIB ├── src/ └── build/进入project/目录执行smibd -I ./mibs/ -I C:\Program Files\MG-SOFT\mibs\standard \ -o ./src/mib_generated/ \ -c ./build/mib_compile.log \ -v 2 \ ./mibs/MY-DEVICE-MIB.mib逐参数拆解其真实作用-I ./mibs/告诉smibd在./mibs/目录下查找被IMPORTS引用的其他 MIB如HUAWEI-UPS-MIB.mib。可叠加多个-I路径间用空格分隔。-I C:\Program Files\MG-SOFT\mibs\standard引入 MG-SOFT 自带的标准 MIB 库。注意路径含空格必须加英文双引号否则 Windows 下解析中断。-o ./src/mib_generated/指定输出目录。smibd会在此目录下创建MY-DEVICE-MIB/子文件夹并放入所有生成文件。该目录必须预先存在smibd不会自动创建。-c ./build/mib_compile.log输出详细编译日志。日志里会显示每条 OID 的解析过程、警告如WARNING: OBJECT-TYPE mySensorTemp has no DESCRIPTION clause、错误如ERROR: Unknown type DisplayString。这是排错唯一依据。-v 2日志详细级别。-v 1只显示错误-v 2显示警告错误-v 3还显示调试信息如 OID 解析树遍历过程。日常开发用-v 2足够。./mibs/MY-DEVICE-MIB.mib主输入文件。只能指定一个主 MIB 文件其他依赖 MIB 必须通过-I提供不能列在命令末尾。2.3 输出文件详解哪些必须改哪些绝对不能碰smibd成功执行后./src/mib_generated/MY-DEVICE-MIB/目录下会生成 5 类文件文件名类型是否可编辑关键说明MY-DEVICE-MIB.h头文件✅ 建议修改包含#define宏如#define MY_DEVICE_OID 1,3,6,1,4,1,XXXXX、结构体声明typedef struct {...} myDeviceEntry_t;。OID 宏名可按项目规范重命名但数值不能改。MY-DEVICE-MIB.c实现文件✅ 必须修改核心mgsoft_get_XXX()/mgsoft_set_XXX()回调函数桩。这里填业务逻辑如读取 STM32 ADC 值、写入 GPIO 状态。初始内容全是return MGSOFT_ERR_GENERR;需替换。MY-DEVICE-MIB_oid.cOID 映射表❌ 禁止修改自动生成的 OID 数组格式{ {1,3,6,1,4,1,XXXXX,1,1,1,0}, TYPE_INTEGER, mgsoft_get_mySensorTemp, ... }。任何手动修改都会导致 Agent 启动时报OID table mismatch。MY-DEVICE-MIB_init.c初始化函数✅ 可选修改void MY_DEVICE_MIB_init(void)负责注册 OID 表到 MG-SOFT 栈。若需动态加载如插件式 MIB可在此函数内加条件判断。MY-DEVICE-MIB.mgcMG-SOFT 中间格式⚠️ 仅用于 GUI 工具二进制文件mgsoft_agent.exeGUI 版可直接加载查看。嵌入式固件编译时完全不需要此文件。特别提醒一个高频陷阱MY-DEVICE-MIB.c中的回调函数名如mgsoft_get_mySensorTemp必须与 MIB 文件中OBJECT-TYPE的SYNTAX和ACCESS严格匹配。例如mySensorTemp OBJECT-TYPE SYNTAX INTEGER (0..100) ACCESS read-only STATUS current DESCRIPTION Current temperature reading :: { myDeviceGroup 1 }则smibd生成的函数签名必为int mgsoft_get_mySensorTemp(int *value)。如果你在 C 文件里改成int mgsoft_get_mySensorTemp(float *value)编译能过但运行时mgsoft_agent.exe会因类型不匹配崩溃。2.4 处理多 MIB 依赖华为 UPS MIB 的实战编译链以热搜词“truenascale 自带ups服务 snmp ups”和“华为 snmp实验”为背景假设你要集成华为 UPS 的HUAWEI-UPS-MIB.mib常见于 NetEco 管理平台配套设备。它依赖SNMPv2-SMI.mib、SNMPv2-TC.mib、IF-MIB.mib且自身定义了私有 OID1.3.6.1.4.1.2011.2.11.1。正确编译链如下# 第一步编译基础标准 MIB只需一次生成通用头文件 smibd -I C:\Program Files\MG-SOFT\mibs\standard \ -o ./src/mib_std/ \ C:\Program Files\MG-SOFT\mibs\standard\SNMPv2-SMI.mib # 第二步编译华为 MIB显式引入所有依赖 smibd -I ./mibs/vendor-specific/ \ -I ./src/mib_std/ \ -I C:\Program Files\MG-SOFT\mibs\standard \ -o ./src/mib_generated/huawei-ups/ \ ./mibs/vendor-specific/HUAWEI-UPS-MIB.mib关键点在于-I ./src/mib_std/必须放在-I C:\Program Files\...之前。因为HUAWEI-UPS-MIB.mib的IMPORTS语句中SNMPv2-SMI的路径优先级高于系统路径。如果顺序颠倒smibd会尝试从标准路径重新解析SNMPv2-SMI.mib导致重复定义错误ERROR: Duplicate definition of module SNMPv2-SMI。编译后huawei-ups/目录下会生成HUAWEI-UPS-MIB.c其中mgsoft_get_upsInputVoltage()函数返回int类型。但华为文档明确要求该 OID 返回Gauge320~4294967295。此时不能改函数签名而应在函数体内做类型转换int mgsoft_get_upsInputVoltage(int *value) { uint32_t raw_voltage read_huawei_ups_register(0x100); // 假设读取寄存器 *value (int)(raw_voltage % 2147483647); // 强制截断到 int 范围避免溢出 return MGSOFT_SUCCESS; }这是 MG-SOFT 的硬性限制所有标量get回调只接受int *参数。遇到Counter32、Gauge32等大数值类型必须在业务逻辑层处理截断或缩放。3. 集成到 STM32 工程从 MG-SOFT 代码到裸机固件的移植要点MG-SOFT 官方示例工程基于 Windows VC但绝大多数工业场景如热搜词“stm32 snmp trap v2c 代码”需要跑在 Cortex-M 系列 MCU 上。我把 MG-SOFT v5.2.1 移植到 STM32F407VGFreeRTOS HAL 库的过程总结为三个不可跳过的技术关卡。3.1 内存模型适配堆空间不是越大越好MG-SOFT 协议栈内部使用动态内存分配malloc/free管理 PDU 缓冲区、OID 树节点等。在裸机环境下必须提供符合要求的malloc实现。STM32CubeMX 生成的heap_4.cCMSIS-RTOS v1是首选但需调整两个关键参数configTOTAL_HEAP_SIZE官方建议 ≥ 16KB但实测发现仅支持 SNMPv2c GET/GETNEXT最低需8KB需支持 SET 操作 Trap 发送必须 ≥12KB若启用 SNMPv3 加密非本主题范围需 ≥ 24KB提示smibd生成的 OID 表大小与 MIB 复杂度正相关。一个含 50 个标量 20 个表格的 MIB编译后*_oid.c文件约 15KB但运行时内存占用仅与当前活跃请求相关非全量加载。heapSTRUCT_SIZEheap_4.c中BlockLink_t结构体大小。MG-SOFT 的mgsoft_malloc对齐要求为 4 字节而heap_4.c默认 8 字节对齐。若不修改会导致mgsoft_free释放失败内存泄漏。在heap_4.c中找到#define portBYTE_ALIGNMENT 8改为#define portBYTE_ALIGNMENT 43.2 网络栈对接LwIP raw API 的精准钩子位置MG-SOFT 不绑定特定网络栈通过mgsoft_netif.h定义的 4 个函数接口与底层通信接口函数作用STM32 LwIP 实现要点mgsoft_netif_init()网络初始化在MX_LWIP_Init()后调用注册netif结构体指针到 MG-SOFT 内部。mgsoft_netif_send()发送 UDP 数据包调用pbuf_alloc(PBUF_TRANSPORT, len, PBUF_RAM)分配缓冲区pbuf_copy_partial(pbuf, data, len, 0)复制数据udp_sendto(netif, pbuf, dest_addr, dest_port)发送。必须检查pbuf_alloc返回非 NULL否则mgsoft_agent会卡死。mgsoft_netif_recv()接收 UDP 数据包在 LwIPudp_recv回调函数中触发。关键mgsoft_netif_recv必须在中断上下文外调用如 FreeRTOS 任务中不能在udp_recv回调里直接调用否则重入风险。mgsoft_netif_get_ip()获取本地 IP直接返回netif-ip_addr.addr注意字节序转换ntohl(netif-ip_addr.addr)。最易出错的是mgsoft_netif_recv的调用时机。常见错误写法// ❌ 错误在 udp_recv 回调里直接调用 void udp_recv_callback(void *arg, struct udp_pcb *pcb, struct pbuf *p, const ip_addr_t *addr, u16_t port) { mgsoft_netif_recv(p-payload, p-len); // 危险pbuf 可能被 LwIP 释放 pbuf_free(p); }正确做法是用队列传递// ✅ 正确通过消息队列解耦 QueueHandle_t xSnmpRxQueue; void udp_recv_callback(...) { // 复制数据到独立缓冲区 uint8_t *rx_buf pvPortMalloc(p-len); pbuf_copy_partial(p, rx_buf, p-len, 0); // 发送到 SNMP 任务 xQueueSend(xSnmpRxQueue, rx_buf, 0); pbuf_free(p); } // SNMP 任务循环 void vSnmpTask(void *pvParameters) { uint8_t *rx_data; while(1) { if(xQueueReceive(xSnmpRxQueue, rx_data, portMAX_DELAY) pdTRUE) { mgsoft_netif_recv(rx_data, len); // 安全调用 vPortFree(rx_data); } } }3.3 Trap 发送实战V2C 协议下如何构造合法 Trap PDU热搜词“stm32 snmp trap v2c 代码”直指痛点。MG-SOFT 的 Trap 发送接口mgsoft_send_trap()看似简单但参数设置稍有偏差接收端如 iReasoning MIB Browser就显示Invalid PDU type。以发送coldStart(0)Trap 为例标准 V2C Trap PDU 结构为SEQUENCE { version: SNMPv2c (1) community: public data: TRAP-TYPE { enterprise: 1.3.6.1.4.1.2011 // 华为私有 OID agent-addr: 192.168.1.100 generic-trap: coldStart(0) specific-trap: 0 time-stamp: 1234567890 variable-bindings: [] } }MG-SOFT 要求用mgsoft_trap_info_t结构体填充mgsoft_trap_info_t trap_info; memset(trap_info, 0, sizeof(trap_info)); // 必填字段缺一不可 trap_info.version MGSOFT_SNMP_VERSION_2C; // 显式指定 V2C strcpy(trap_info.community, public); // 社区字符串长度≤32 trap_info.enterprise_oid[0] 1; // 华为 OID 1.3.6.1.4.1.2011 trap_info.enterprise_oid[1] 3; trap_info.enterprise_oid[2] 6; trap_info.enterprise_oid[3] 1; trap_info.enterprise_oid[4] 4; trap_info.enterprise_oid[5] 1; trap_info.enterprise_oid[6] 2011; trap_info.enterprise_oid_len 7; // OID 长度必须精确 // agent-addr必须是 uint32_t 网络字节序 trap_info.agent_addr htonl(IPADDR_BYTE4(ipaddr_get_ip4_u32(netif-ip_addr))); // generic-trapcoldStart0, warmStart1, linkDown2, linkUp3... trap_info.generic_trap 0; // time-stamp单位为 1/100 秒需从系统启动时间计算 trap_info.time_stamp (uint32_t)(xTaskGetTickCount() * 10); // FreeRTOS tick 10ms // variable-bindings空数组也要初始化 trap_info.varbinds NULL; trap_info.varbinds_count 0; // 发送 mgsoft_send_trap(trap_info);注意enterprise_oid_len必须等于实际 OID 节点数。填6会导致mgsoft_send_trap返回MGSOFT_ERR_INVALID_PARAM且无日志提示。这是文档未明说的隐式约束。4. 故障排查全景图从 Wireshark 抓包到 MG-SOFT 日志的闭环分析法当 SNMP Agent 不响应 GET 请求或 Trap 发不出去时90% 的人只会重启设备、重刷固件、怀疑网线。真正的排错必须建立“抓包 → 协议栈日志 → 业务代码”三级证据链。我用这套方法在客户现场 15 分钟内定位过 7 类典型故障。4.1 第一层Wireshark 抓包确认网络层可达性在 Agent 设备同一局域网的 PC 上用 Wireshark 过滤udp.port 161 || udp.port 162观察三个关键帧帧类型正常表现异常表现及根因Manager → Agent (GET)Source: 192.168.1.50:51234, Dest: 192.168.1.100:161, Length: 68无此帧 → Manager 端配置错误IP/社区名或防火墙拦截Agent → Manager (RESPONSE)Source: 192.168.1.100:161, Dest: 192.168.1.50:51234, Length: 82有 Request 无 Response → Agent 未收到包LwIP 配置错误或mgsoft_netif_recv未触发Agent → Manager (TRAP)Source: 192.168.1.100:162, Dest: 192.168.1.50:162, Length: 105无 Trap 帧 →mgsoft_send_trap()调用失败或mgsoft_netif_send()返回错误特别注意Wireshark 显示SNMP协议解析失败显示为UDP说明 PDU 结构非法。此时必须导出该 UDP 包的 Raw Data用十六进制比对标准 SNMPv2c PDU 格式BER 编码。MG-SOFT 生成的 Trap PDU第一个字节必为0x30SEQUENCE tag第二个字节为长度字段。若为0x02INTEGER tag说明version字段填错。4.2 第二层MG-SOFT 内部日志开关与解读MG-SOFT 提供mgsoft_debug.h中的宏控制日志输出。在main.c中添加#define MGSOFT_DEBUG_LEVEL 3 // 0off, 1error, 2warnerror, 3all #include mgsoft_debug.h然后重编译固件。日志输出到串口printf重定向到 UART关键线索如下日志关键词含义应对措施mgsoft_pdu_parse: invalid versionPDU version 字段错误如填了 0 当作 v1检查mgsoft_trap_info_t.version或mgsoft_set_version()调用mgsoft_oid_lookup: OID not found请求的 OID 未在*_oid.c表中注册确认smibd编译时主 MIB 文件路径正确且MY_DEVICE_MIB_init()已调用mgsoft_netif_send: send failed, err-1udp_sendto()返回 ERR_MEM内存不足或 ERR_RTE路由不可达检查 LwIPnetif是否netif_add()成功netif_set_up()是否调用mgsoft_get_xxx: not implementedmgsoft_get_XXX()函数内未修改返回值仍为MGSOFT_ERR_GENERR打开*_mib.c找到对应函数填入业务逻辑提示MGSOFT_DEBUG_LEVEL 3会产生海量日志每秒数百行建议用screen或Tera Term的日志过滤功能关键词搜索err、fail、not found。4.3 第三层业务代码断点验证以 STM32 Keil MDK 为例当 Wireshark 和日志都指向“Agent 收到了请求但没处理”问题一定在mgsoft_get_XXX()函数内。Keil 调试步骤在mgsoft_get_sysUpTime()函数首行设断点用snmpget -v2c -c public 192.168.1.100 1.3.6.1.2.1.1.3.0发起请求观察断点是否命中未命中→smibd生成的 OID 表未正确注册检查MY_DEVICE_MIB_init()是否被调用mgsoft_register_mib_table()返回值是否为 0命中但立即退出→ 函数内return MGSOFT_ERR_GENERR;未修改或*value未赋值命中且赋值但 Manager 收不到响应→mgsoft_netif_send()失败检查 LwIPpbuf分配是否成功pbuf_alloc返回 NULL 通常因heap不足。一个真实案例某客户设备sysUpTime总是返回 0。调试发现mgsoft_get_sysUpTime()中*value uptime_ms / 10;但uptime_ms是uint32_t除以 10 后赋给int *value当uptime_ms 21474836470约 68 年时溢出为负数。解决方案*value (int)(uptime_ms / 10);强制类型转换。5. 进阶技巧MIB 编译自动化与 CI/CD 集成实践在量产项目中每次改 MIB 都手动敲smibd命令、检查日志、复制文件效率极低。我们团队将整个流程接入 GitLab CI实现“MIB 提交 → 自动编译 → 代码审查 → 固件构建”闭环。以下是可直接复用的.gitlab-ci.yml片段stages: - mib-compile - firmware-build mib-compile-job: stage: mib-compile image: name: registry.gitlab.com/mgsoft-ci/docker-smibd:5.2.1 entrypoint: [] script: - export PATH/opt/mgsoft/bin:$PATH - cd $CI_PROJECT_DIR # 清理旧生成文件 - rm -rf src/mib_generated/ # 编译主 MIB - smibd -I mibs/ -I /opt/mgsoft/mibs/standard -o src/mib_generated/ -v 2 mibs/MY-DEVICE-MIB.mib - echo MIB compile completed artifacts: paths: - src/mib_generated/ expire_in: 1 week firmware-build-job: stage: firmware-build image: armcc/gcc-arm-none-eabi needs: [mib-compile-job] script: - cd $CI_PROJECT_DIR - make clean - make all artifacts: paths: - build/firmware.bin配套的 Docker 镜像registry.gitlab.com/mgsoft-ci/docker-smibd:5.2.1构建脚本核心FROM ubuntu:20.04 RUN apt-get update apt-get install -y wget unzip # 下载 MG-SOFT for Linux需从官网获取授权 RUN wget https://example.com/mgsoft-linux-v5.2.1.zip \ unzip mgsoft-linux-v5.2.1.zip -d /opt/mgsoft \ chmod x /opt/mgsoft/bin/smibd ENV PATH/opt/mgsoft/bin:$PATH注意MG-SOFT Linux 版需单独购买授权不能直接用 Windows 版smibd.exe Wine。官方明确不支持 Wine 环境编译。另一个实用技巧用 Python 脚本自动校验 MIB 语法并生成编译报告。以下check_mib.py可集成到 pre-commit hook#!/usr/bin/env python3 import subprocess import sys import os def run_smibd(mib_path): result subprocess.run( [smibd, -I, ./mibs/, -I, /opt/mgsoft/mibs/standard, -o, /tmp/mib_out/, -v, 2, mib_path], capture_outputTrue, textTrue, timeout60 ) return result.returncode, result.stdout, result.stderr if __name__ __main__: mib_file sys.argv[1] if len(sys.argv) 1 else ./mibs/MY-DEVICE-MIB.mib code, stdout, stderr run_smibd(mib_file) if code 0: print(f✅ MIB {mib_file} compiled successfully) sys.exit(0) else: print(f❌ MIB compilation failed for {mib_file}) print(STDERR:, stderr) # 提取关键错误行 errors [line for line in stderr.split(\n) if ERROR: in line] for e in errors[:3]: # 只显示前3个错误 print( , e) sys.exit(1)运行python check_mib.py ./mibs/HUAWEI-UPS-MIB.mib失败时直接打印ERROR: Unknown type DisplayString提示你缺少SNMPv2-TC.mib依赖——比翻日志快 10 倍。最后分享一个血泪经验永远不要在smibd命令中使用相对路径的-I参数指向网络共享盘如\\server\mibs\。Windows 下smibd会因 UNC 路径解析失败静默退出返回码 0 但无输出文件。必须映射为本地盘符如Z:再用-I Z:\mibs\。这个坑我花了两天才从 Process Monitor 的 API 调用日志里挖出来。