news 2026/9/18 20:05:25

CANN Runtime 错误消息(ErrMsg)文案模板详解:EE/EH 系列错误码的 Arglist、Reason 与整改示例

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
CANN Runtime 错误消息(ErrMsg)文案模板详解:EE/EH 系列错误码的 Arglist、Reason 与整改示例

CANN Runtime 错误消息(ErrMsg)文案模板详解:EE/EH 系列错误码的 Arglist、Reason 与整改示例

【免费下载链接】runtime本项目提供CANN运行时组件和维测功能组件。项目地址: https://gitcode.com/cann/runtime

本篇基于 CANN Runtime 仓库中的 ErrMessage 翻译总表,系统讲解 Runtime(EE 系列)与 ACL(EH 系列)错误消息的文案模板、参数列表(Arglist)与各错误码的典型 Reason 取值,并结合 error_code.json、error_code_meta.h 等事实来源,说明这些模板如何被上报宏填充、最终呈现给用户。读完后,你可以准确解读一条带ErrorCode=EExxxx的错误输出,也知道在整改或新增错误文案时应如何选择参数与措辞。

一、错误消息体系速览:模板、Arglist 与 Reason

CANN Runtime 的错误消息(ErrMsg)不是随意的日志字符串,而是"模板 + 参数"的结构化文本。每个外部错误码(后四位为 0001~8999 的码)在 error_code.json 中定义,核心字段为:

  • ErrMessage:printf 风格的文案模板,其中的%s/%u等占位符按固定顺序取参;
  • Arglist:占位符对应的参数名列表,如func, value, param, expect
  • suggestion:面向用户的Possible CauseSolution,随错误码一起展示。

上报时,error_code_meta.h 中的 X-Macro 参数表会把error_code.json的模板同步成 C++ 侧格式串,并在末尾追加ErrorCode=EExxxx.后缀。例如 meta.h 中 EE1018 的定义:

X(EE1018, "EE1018", ("func", "reason"), "%s failed. Reason: %s. ErrorCode=EE1018.\n", DLOG_ERROR)

而 message-examples.md 这份"翻译总表",正是对其中一批错误码(EE1003、EE1006、EE1007、EE1009、EE1011、EE1012、EE1014、EE1015、EE1016、EE1017、EE1018、EE9999、EH0009、EH0011)的ErrMessage 模板、Arglist 与 reason 具体描述清单的逐码汇总。下面按错误码逐一展开。

二、参数非法类(Invalid_Argument)

EE1003:值非法且能给出期望值

  • errTitle:Invalid_Argument
  • ErrMessage%s failed because value %s for parameter %s is invalid. Expected value: %s.
  • Arglistfunc, value, param, expect
  • 典型 reason
    1. 参数值与RT_EVENT_FLAG存在互斥(exclusive OR);
    2. 参数值需大于或等于 0。
  • Solution:1. 检查函数输入参数范围;2. 检查函数调用关系。

该码是 Runtime 层"输入参数非法"的首选通用码(在 error-code-guide.md 中标注 ⭐)。func参数建议传入语义化描述而非__func__。例如仓库中的实际用法(引自 error-code-guide.md 的示例):

RT_LOG_OUTER_MSG_WITH_FUNC_DESC(ErrorCode::EE1003, "Obtaining the logical device ID based on the user device ID", userDevId, "userDevId", "[0, " + std::to_string(userDeviceCnt) + ")");

打屏效果:Obtaining the logical device ID based on the user device ID failed because value 8 for parameter userDevId is invalid. Expected value: [0, 4). ErrorCode=EE1003.

EE1011 vs EE1012:同 Arglist、不同模板

两者 Arglist 完全相同(func, value, param, reason),仅文案模板存在一词之差:

错误码ErrMessage 模板第 3 个参数前的措辞
EE1011%s failed. Value %s for parameter %s is invalid. Reason: %s.for parameter %s
EE1012%s failed. Value %s for %s is invalid. Reason: %s.for %s(省去 parameter)

EE1011 典型 reason(翻译总表原文,节选)

  1. 非持久流%u不支持清空流任务(Non-persistent stream does not support stream task clearance);
  2. %u必须绑定到模型(Stream %u must be bound to a model);
  3. AI CPU 流%u不支持清空流任务;
  4. count不能超过最大值destMax %" PRIu64 "
  5. memcpyAddrInfo未做 64 字节对齐;
  6. 无法通过stubFunc找到对应 kernel——指定的函数地址无效或 kernel 状态异常;
  7. 无法通过tilingKey找到对应 kernel——tilingKey 无效或 kernel 状态异常;
  8. %d不属于当前 context;
  9. 流未绑定到模型(The stream is not bound to a model);
  10. ACL_STREAM_DEVICE_USE_ONLYflag 的流不能绑定到模型。

EE1012 典型 reason

  1. 当前 device 无法下发 Notify Wait——对应的 Notify Wait 必须在创建 IPC Notify 的 device 上下发。

EE1012 的 suggestion 中Possible Cause标注为 "The host memory is insufficient"(注意:这是总表原文的字段内容,属于该码预置 suggestion,而非每个场景的通用结论)。

选择规则(与 error-code-guide.md 的决策树一致):把第 3 个参数代入两种模板,读起来哪个自然用哪个。参数名是普通单词(如deviceId)时优先 EE1011;参数名本身已是描述性短语(如queue name length)时用 EE1012,避免 "for parameter queue name length" 的冗余。仓库中 EE1011 的真实调用示例:

// src/acl 层对应码为 EH0009,Runtime 层示例: kernel = Runtime::Instance()->KernelLookup(stubFunc); COND_RETURN_AND_MSG_OUTER(kernel == nullptr, RT_ERROR_KERNEL_NULL, ErrorCode::EE1011, __func__, static_cast<const char_t *>(stubFunc), "stubFunc", "The corresponding kernel cannot be found through stubFunc. " "The specified function address is invalid or the kernel status is abnormal");

打屏效果:rtKernelLaunch failed. Value "kernel_Add" for parameter stubFunc is invalid. Reason: The corresponding kernel cannot be found through stubFunc.... ErrorCode=EE1011.

EE1017:无法给出参数具体值

  • errTitle:Invalid_Argument
  • ErrMessage%s failed. Parameter %s is invalid. Reason: %s.
  • Arglistfunc, param, reason(注意:没有value,因为该场景下拿不到或不应打印参数具体值)
  • Solution:无(suggestion 为 N/A)

翻译总表给出了 15 条典型 reason,覆盖模型/流/label 的归属与状态校验,节选:

  1. %u所在的模型%u尚未加载,需在模型加载后再清空流任务;
  2. 指定地址必须是 device 地址;
  3. 无法通过 device ID%d、stream ID%u、task ID%u三元组找到对应任务;
  4. 配置中待更新数据存放的 device 内存地址与当前指定地址不一致,多次任务更新必须使用相同的 device 内存地址;
  5. 与 label[%u]关联的流%d不在当前 context 中;
  6. 与 label[%u]关联的流%d不在模型中,需先调用rtLabelSet将流绑定到模型;
  7. 与 label[%u]关联的流和 label[0]关联的流不属于同一模型;
  8. 仅随机数生成任务支持该更新操作;
  9. argHandlepara.type为 place holder 的参数个数(%u)必须小于%u
  10. 绑定到流的模型%u与 label 所属模型%u不一致;
  11. 当前流%u与 label 关联的流%u不一致;
  12. 持久流(persistent stream)不支持查询任务执行状态;
  13. 回调函数fn已注册,不能重复注册;
  14. 回调函数fn尚未注册;
  15. 当前任务类型不支持该操作。

EE1017 的适用前提是"无法给出参数具体值"——例如参数是句柄/对象属性,或其取值本身无诊断意义;若两个及以上用户参数间关系不满足(如要求 A < B),也推荐用 EE1017,把具体参数值和关系写进 Reason。

EH0009:ACL 层对应码(参数非法 + 原因)

  • errTitle:Invalid_Argument
  • ErrMessage%s failed. Value %s for parameter %s is invalid. Reason: %s.
  • Arglistfunc, value, param, reason
  • Solution:1. 检查函数输入参数范围;2. 检查函数调用关系。

翻译总表列出的典型 reason:

  1. 该流未注册到任何 allocator(The stream is not registered with any allocator);
  2. 指定参数%d短于算力组(computing power group)信息的长度,无法保存该信息;
  3. 当前数据类型不支持;
  4. 当前物理内存属性不支持。

EH0009 与 EE1011 的模板完全一致,层级不同:EE 用于src/runtime/代码,EH 用于src/acl/代码。ACL 层的真实调用示例(引自 macro-selection-guide.md 中 EH0009 专用宏说明):

// ACL_CHECK_INVALID_PARAM_WITH_REASON 系列宏上报 EH0009 acl::AclErrorLogManager::ReportInputError(acl::INVALID_PARAM_REASON_MSG, {"func", "value", "param", "reason"}, {"Checking the synchronous memory copy parameter validity", widthVal.c_str(), "width", errMsg.c_str()}); // 输出:Checking the synchronous memory copy parameter validity failed. // Value 2048 for parameter width is invalid. Reason: must be less than spitch and dpitch. // ErrorCode=EH0009.

三、不支持类(Not_Supported)

EE1006:配置参数级不支持(三参数模板)

  • errTitle:Not_Supported
  • ErrMessage%s failed. %s is not supported. Reason: %s.
  • Arglistfunc, type, reason
  • suggestion.Possible Cause
    1. 当前 CANN 软件版本不支持;
    2. 当前驱动软件版本不支持;
    3. 当前芯片版本不支持。
  • suggestion.Solution:1. 升级 CANN 软件版本;2. 升级驱动软件版本。

翻译总表列出了 8 条典型 reason,是理解"流/事件/模型运行时哪些操作被禁止"的好材料:

  1. OFFLINE 模式下不支持 P2P 内存类型;
  2. 融合任务仅支持 HCOMM+AI Core、AI CPU+AI Core、CCU+AIC 的组合,或单个 CCU 任务;
  3. 当前 SoC 仅支持任务数正常的流,不支持巨量任务流(huge stream);
  4. Device-only event 只能在 device 上调用;
  5. 当前流用于承载 AI CPU 调度任务,不支持设置优先级;
  6. 当前 SoC 不支持该数据类型的 reduce 操作;
  7. 当前 SoC 不支持 P2P 内存分配;
  8. "仅分配巨页内存"策略与"分配底层 cache 内存"策略冲突。

使用要点:若不支持可通过升级解决,Reason 中应写明支持的软件版本信息;type参数优先使用语义化描述。仓库中的真实调用(节选自 api_error.cc 中的 EE1006 调用点,如 stream flag 检查):

COND_RETURN_AND_MSG_OUTER((stm != nullptr) && ((stm->Flags() & RT_STREAM_AICPU) != 0U), RT_ERROR_STREAM_INVALID, ErrorCode::EE1006, "Synchronizing a stream", "Stream flags value " + std::to_string(stm->Flags()), "The current stream is used to carry AI CPU scheduling tasks " "and does not support stream synchronization");

打屏效果:Synchronizing a stream failed. Stream flags value 4 is not supported. Reason: The current stream is used to carry AI CPU scheduling tasks and does not support stream synchronization. ErrorCode=EE1006.

EE1015:驱动版本能力不足

  • errTitle:Package_Error_Incorrect_Driver_Version
  • ErrMessage%s failed. Reason: The driver version capability is insufficient. %s
  • Arglistfunc, reason
  • 典型 reason:当前版本%u早于要求的版本%u
  • Solution:升级驱动软件版本。

该码专用于"升级驱动包即可解决"的场景,例如驱动.so中找不到所需符号:

COND_RETURN_AND_MSG_OUTER(tsdOpenNetService_ == nullptr, RT_ERROR_DRV_TSD_ERR, ErrorCode::EE1015, "Starting the HCCP process", "Symbol TsdOpenNetService not found in libtsdclient.so.");

打屏效果:Starting the HCCP process failed. Reason: The driver version capability is insufficient. Symbol TsdOpenNetService not found in libtsdclient.so. ErrorCode=EE1015.

EE1016:场景/功能不支持(两参数模板)

  • errTitle:Not_Supported
  • ErrMessage%s failed. Reason: %s.
  • Arglistfunc, reason

翻译总表给出的 3 条典型 reason 均与ACL Graph 捕获模式相关:

  1. 当前 context 的其他线程处于捕获状态,当前线程无法执行该操作;可调用aclmdlRICaptureThreadExchangeMode切换捕获模式(模板给出aclmdlRICaptureBegin设置的模式、当前线程模式、aclmdlRICaptureThreadExchangeMode设置的模式三个值);
  2. 当前线程%d处于捕获模式且当前操作不被支持——只有 RELAXED 模式支持该操作;
  3. 其他线程处于捕获状态的紧凑变体:contextCaptureMode=%d, threadCaptureMode=%d, exchangeCaptureMode=%d
  • Solution:检查aclmdlRICaptureBegin设置的模式是否支持当前线程中的当前操作。

EE1016 覆盖两类"固有不支持":某状态/场景下不支持某类操作(如从 stop 模式切到 continue 模式),以及与芯片无关的完整功能不支持。capture mode 场景有专用宏CHECK_CAPTURE_MODE_SUPPORT_AND_RETURN[_WITH_DESC],其内部通过RT_LOG_OUTER_MSG_IMPL(ErrorCode::EE1016, funcName, reason)完成上报。

EH0011:ACL 层"芯片不支持"专用码

  • errTitle:Not_Supported
  • ErrMessageThe current system or device does not support %s.
  • Arglistfunc(无 reason 字段)
  • 典型 reason(体现在 func 参数文案中):仅支持 Ascend 910 芯片。

EH0011 与 EE1005 模板一致(The current system or device does not support %s.),用于"换芯片可解决"的场景。ACL 层真实示例(引自 macro-selection-guide 与 error-code-guide 的示例):

acl::AclErrorLogManager::ReportInputError(acl::UNSUPPORTED_SYSTEM_MSG, {"func"}, {"aclrtSetDeviceWithoutTsdVXX, only Ascend 910 chips are supported"});

注意:非芯片原因的不支持(配置参数、软件版本、固有限制)在 ACL 层应使用 EH0006 而非 EH0011。

四、资源与执行类

EE1007:流绑定模型失败

  • errTitle:Resource_Error_Bind_Stream
  • ErrMessage(总表版本):Failed to bind stream with ID %s. Reason: %s.;error_code.json 与 error_code_meta.h 中的现行模板为Failed to bind stream (stream_id=%s). Reason: %s.
  • Arglistid, reason
  • 典型 reason
    1. 流绑定失败,stm参数不能是指定 flag(%u)的流;
    2. 非持久流不能绑定到模型;
    3. 该流绑定了多个 mdlRI(Size: %u);
    4. 该流已被绑定;
    5. AI CPU 流被复用;
    6. 模型已绑定到另一个流。
  • Solution:先将流从已绑定的模型上解绑,再绑定到当前模型。

仓库真实调用示例(模型输入流绑定检查):

if (streamIn->IsModelStream()) { RT_LOG_OUTER_MSG_IMPL(ErrorCode::EE1007, streamId, RtFmtMsg("The current stream has been bound to a model (model_id=%u) " "and cannot be bound to the input model (model_id=%u)", streamIn->Model_()->Id_(), Id_())); return RT_ERROR_STREAM_MODEL; }

EE1009:模型执行失败

  • errTitle:Execution_Error_Model
  • ErrMessage(总表版本):Failed to execute model with ID %s. Reason: %s.;现行模板为Failed to execute model (model_id=%s). Reason: %s.
  • Arglistid, reason(id 为模型 ID)
  • 典型 reason
    1. 当前流不能与模型流相同;
    2. 指定 flag(%u)的流不能用于模型执行;
    3. 当前 ACL Graph 模型运行实例既不包含任何可执行任务,也不包含任何可执行流。

示例:Failed to execute model (model_id=1). Reason: The current aclgraph model running instance neither contains any executable task nor contains any executable stream. ErrorCode=EE1009.

EE9999:内部错误(无模板,仅原始消息)

EE9999 是 Runtime 内部错误码,没有 JSON 模板——总表为其预留了空的 errTitle/ErrMessage/Arglist 字段,仅汇总典型 reason 清单,共 40 条,反映的是内部逻辑断言的常见形态。上报时格式为XX9999: Inner Error!加原始消息(由_INNER系列宏写入,如COND_RETURN_AND_MSG_INNER)。其 reason 清单可视为"内部第一现场"的故障字典,节选:

  1. 模块加载失败:程序大小应为大于 0 的值,实际为 0;
  2. rtArgsEx_t.args分配 device 内存失败;
  3. 基于 SO 名获取 SO 地址失败;
  4. 当前流状态不满足下发任务的条件;
  5. 任务提交后的后处理失败;
  6. label 列表中已存在相同 label;
  7. label 信息从 host 拷贝到 device 失败;
  8. Label ID%u被重复释放;
  9. devDstAddr被重复设置;
  10. 当前线程与执行StreamBeginCapture的线程不同;
  11. 模型执行前设置 notify 失败;
  12. 等待流内所有任务完成失败;
  13. 流与模型解绑失败——指定流未绑定到当前模型;
  14. 模型不包含任何流;
  15. str长度必须大于 0;
  16. 流中最后一个任务类型不是 event record;
  17. 捕获事件尚未被记录;
  18. 重新申请的 SQ/CQ/逻辑 CQ 与原始值不一致;
  19. 当前流的 SQ 和 CQ 已申请过,不能再次申请;
  20. 远端 SQ 不能被复用;
  21. 订阅同步调度的线程数超过最大值%u
  22. TS 状态异常;
  23. Device%u故障;
  24. 流状态为%u
  25. 模型流已满;
  26. sendSqenum的值%u不能大于任务允许的最大 SQE 数(%u);
  27. SQE 总数(%u)不能大于 SQ 深度%u
  28. Device%u不可用;
  29. 任务数不能超出任务组大小;
  30. 清理任务期间必须禁用 SQ;
  31. DQS 流不能用ts_id %u创建,应使用ts_id %u
  32. qid %u对应的 mbuf pool 信息不存在,检查配置流程;
  33. 被依赖的程序可能已被释放;
  34. 当前 label 已被设置到另一个流;
  35. 从 device 查询到的 abort 状态无效;
  36. 流回收超时;
  37. 任务回收失败;
  38. 绑定到当前流%u的 ACL Graph 模型%u不满足更新条件——运行或捕获状态的 ACL Graph 模型均不可更新;
  39. 下发 CmoAddr 任务的流不在模型中;
  40. 指定 DVPP 组的流%d不能绑定到模型。

从源码结构看,内部错误码的获取规则是:一次错误获取中若混合了外部/内部错误码,优先取第一条外部错误码作为"首错"(带 title/cause/solution),其余进入 TraceBack;若全部是内部错误码,则第一条作为首错(XX9999: Inner Error!),其余进入 TraceBack。

五、文件解析与整改要点

EE1014:算子 ELF 二进制解析失败

  • errTitle:File_Operation_Error_Parse
  • ErrMessageFailed to parse the binary file of the operator. Reason: %s.
  • Arglistreason
  • suggestion.Possible Cause:1. 算子二进制文件损坏;2. 构建参数不正确。
  • suggestion.Solution:重新构建并加载算子二进制文件。

翻译总表列出的 10 条典型 reason 精确对应 ELF 文件头与 section header 的校验逻辑,是排查算子.so/bin 损坏的直接依据:

  1. 算子二进制 ELF 文件头中e_shentsize的值%ue_shnum的值%u不正确——两者均不能为 0,且乘积不能超过uint64_t最大值;
  2. ELF 文件头中e_shentsize的值%u必须等于 ELF section header 的大小%u
  3. ELF section header 地址不能为空;
  4. 排名%u的 section 的偏移量%u超出 ELF 对象大小%u
  5. 排名%u的 section 的sh_link%u无效,有效范围[%u, %u]
  6. section->sh_entsize的值%lu无效,有效范围(0, %lu]
  7. section->sh_entsize的值%zu无效,必须大于或等于%u
  8. 排名%ush_ent偏移量%u超出 ELF 对象大小%u
  9. ELF 文件必须是 64 位文件;
  10. 获取 meta section 失败:kernelName=%s, meta type=%u

该码只能用于算子 ELF bin 解析失败;其他文件操作场景(如路径不可访问)误用会输出误导性的 "Failed to parse the binary file of the operator" 前缀。

文案整改的三条硬约束

结合总表所在目录的 README、rectification-principles.md 与 review-checklist.md,使用/修改这些模板时需满足:

  1. 参数数量严格一致:宏调用传入的参数个数必须等于 Arglist 长度,%s占位符与参数按序一一对应(EE1003 为 4 个,EE1017 为 3 个,EE1014 只有 1 个 reason)。
  2. 双源同步:错误码元数据以 error_code.json 为准,修改模板时须同步 error_code_meta.h(X-Macro 表);总表中的"new ErrMessage"与 json/meta 的现行措辞可能因整改进度略有差异(如 EE1007 的 "with ID %s" 与 "stream_id=%s"),以代码为准
  3. 参数拼接用RtFmtMsg_OUTER宏的参数需转换为 string,动态内容禁止std::string直接相加(会导致 .so 膨胀),应使用RtFmtMsg("Changing stream %u is not supported", id)之类的栈上格式化。

六、如何阅读一条实际的错误输出

把总表倒过来用即可定位问题。以一条真实形态的输出为例:

Setting the stream error reporting mode failed. Reason: Changing stream 15 from stop mode to continue mode is not supported. ErrorCode=EE1016.
  1. ErrorCode=EE1016查总表 → errTitle 为 Not_Supported,Arglist 为func, reason
  2. 第一个%s填了 "Setting the stream error reporting mode"(语义化 func 描述);
  3. Reason:之后到ErrorCode之前整段是 reason 参数;
  4. 该码 suggestion 为 N/A,处理方向由 reason 本身指示(固有不支持,无需升级)。

对照之下,Expected value:结尾的 EE1003、%s is not supported结构的 EE1006、以及带stream_id=/model_id=资源标识的 EE1007/EE1009,都能按同一方式拆解。

小结与延伸阅读

  • 错误码全量清单、决策树与常见误用示例:error-code-guide.md;
  • 上报宏(COND_RETURN_AND_MSG_OUTERACL_CHECK_INVALID_PARAM_WITH_REASON等)的选择规则:macro-selection-guide.md;
  • 整改边界、第一现场与打印格式规范:rectification-principles.md;
  • 提交前自检(双源同步、参数数量、文案语法):review-checklist.md;
  • 元数据事实来源:error_code.json、error_code_meta.h、error_manager.h。

【免费下载链接】runtime本项目提供CANN运行时组件和维测功能组件。项目地址: https://gitcode.com/cann/runtime

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/18 19:59:37

Zustand redux 中间件详解:用 Redux 式 action/reducer 驱动状态更新

Zustand redux 中间件详解&#xff1a;用 Redux 式 action/reducer 驱动状态更新 【免费下载链接】zustand &#x1f43b; Bear necessities for state management in React 项目地址: https://gitcode.com/gh_mirrors/zu/zustand redux 是 Zustand 提供的一个官方中间件…

作者头像 李华
网站建设 2026/9/18 19:57:42

概要设计与详细设计:从定骨架到填血肉的完整指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/18 19:57:13

酒店员工管理系统自研实践:排班考勤与数据库设计

简介&#xff1a;一份针对酒店员工管理系统的计算机专业毕业论文设计文档&#xff0c;适合计算机相关专业学生参考选题、结构撰写与系统开发实现。内容围绕SSM框架、MySQL数据库、HTML及B/S架构展开&#xff0c;完整覆盖课题背景、可行性分析、系统流程图、用例图、数据库设计、…

作者头像 李华
网站建设 2026/9/18 19:56:56

游戏压缩包解压报错怎么办?7-Zip、WinRAR、Bandizip搭配使用指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华