OpenHarmony(鸿蒙南向开发)——小型系统内核(LiteOS-A)【Trace调测】

news2024/11/15 11:19:13

往期知识点记录:

  • 鸿蒙(HarmonyOS)应用层开发(北向)知识点汇总
  • 鸿蒙(OpenHarmony)南向开发保姆级知识点汇总~
  • 持续更新中……

基本概念

Trace调测旨在帮助开发者获取内核的运行流程,各个模块、任务的执行顺序,从而可以辅助开发者定位一些时序问题或者了解内核的代码运行过程。

运行机制

内核提供一套Hook框架,将Hook点预埋在各个模块的主要流程中, 在内核启动初期完成Trace功能的初始化,并注册Trace的处理函数到Hook中。

当系统触发到一个Hook点时,Trace模块会对输入信息进行封装,添加Trace帧头信息,包含事件类型、运行的cpuid、运行的任务id、运行的相对时间戳等信息;

Trace提供2种工作模式,离线模式和在线模式。

离线模式会将trace frame记录到预先申请好的循环buffer中。如果循环buffer记录的frame过多则可能出现翻转,会覆盖之前的记录,故保持记录的信息始终是最新的信息。Trace循环buffer的数据可以通过shell命令导出进行详细分析,导出信息已按照时间戳信息完成排序。

在线模式需要配合IDE使用,实时将trace frame记录发送给IDE,IDE端进行解析并可视化展示。

接口说明

内核态

LiteOS-A内核的Trace模块提供下面几种功能,接口详细信息可以查看 API 参考。

表1 Trace模块接口说明

功能分类接口描述
启停控制LOS_TraceStart:启动Trace
LOS_TraceStop:停止Trace
操作Trace记录的数据LOS_TraceRecordDump:输出Trace缓冲区数据
LOS_TraceRecordGet:获取Trace缓冲区的首地址
LOS_TraceReset:清除Trace缓冲区中的事件
过滤Trace记录的数据LOS_TraceEventMaskSet:设置事件掩码,仅记录某些模块的事件
屏蔽某些中断号事件LOS_TraceHwiFilterHookReg:注册过滤特定中断号事件的钩子函数
插桩函数LOS_TRACE_EASY:简易插桩
LOS_TRACE:标准插桩
  • 当用户需要针对自定义事件进行追踪时,可按规则在目标源代码中进行插桩,系统提供如下2种插桩接口:
    • LOS_TRACE_EASY(TYPE, IDENTITY, params…) 简易插桩。
      • 用户在目标源代码中插入该接口即可。
      • TYPE有效取值范围为[0, 0xF],表示不同的事件类型,不同取值表示的含义由用户自定义。
      • IDENTITY类型UINTPTR,表示事件操作的主体对象。
      • Params类型UINTPTR,表示事件的参数。 示例:
            假设需要新增对文件(fd1、fd2)读写操作的简易插桩,
            自定义读操作为type:1, 写操作为type:2,则
            在读fd1文件的适当位置插入:
            LOS_TRACE_EASY(1, fd1, flag, size);
            在读fd2文件的适当位置插入:
            LOS_TRACE_EASY(1, fd2, flag, size);
            在写fd1文件的适当位置插入:
            LOS_TRACE_EASY(2, fd1, flag, size);
            在写fd2文件的适当位置插入:
            LOS_TRACE_EASY(2, fd2,flag, size);
  • LOS_TRACE(TYPE, IDENTITY, params…) 标准插桩。
    • 相比简易插桩,支持动态过滤事件和参数裁剪,但使用上需要用户按规则来扩展。
    • TYPE用于设置具体的事件类型,可以在头文件los_trace.h中的enum LOS_TRACE_TYPE中自定义事件类型。定义方法和规则可以参考其他事件类型。
    • IDENTITY和Params的类型及含义同简易插桩。 示例:
            1.在enum LOS_TRACE_MASK中定义事件掩码,即模块级别的事件类型。
              定义规范为TRACE_#MOD#_FLAG,#MOD#表示模块名。
              例如:
              TRACE_FS_FLAG = 0x4000
            2.在enum LOS_TRACE_TYPE中定义具体事件类型。
              定义规范为#TYPE# = TRACE_#MOD#_FLAG | NUMBER,
              例如:
              FS_READ  = TRACE_FS_FLAG | 0; // 读文件
              FS_WRITE = TRACE_FS_FLAG | 1; // 写文件
            3.定义事件参数。定义规范为#TYPE#_PARAMS(IDENTITY, parma1...) IDENTITY, ...
              其中的#TYPE#就是上面2中的#TYPE#,
              例如:
              #define FS_READ_PARAMS(fp, fd, flag, size)    fp, fd, flag, size
              宏定义的参数对应于Trace缓冲区中记录的事件参数,用户可对任意参数字段进行裁剪:
              当定义为空时,表示不追踪该类型事件:
              #define FS_READ_PARAMS(fp, fd, flag, size) // 不追踪文件读事件
            4.在适当位置插入代码桩。
              定义规范为LOS_TRACE(#TYPE#, #TYPE#_PARAMS(IDENTITY, parma1...))
              LOS_TRACE(FS_READ, fp, fd, flag, size); // 读文件的代码桩,
              #TYPE#之后的入参就是上面3中的FS_READ_PARAMS函数的入参

说明: 预置的Trace事件及参数均可以通过上述方式进行裁剪,参数详见 kernel\include\los_trace.h。

  • Trace Mask事件过滤接口LOS_TraceEventMaskSet(UINT32 mask),其入参mask仅高28位生效(对应LOS_TRACE_MASK中某模块的使能位),仅用于模块的过滤,暂不支持针对某个特定事件的细粒度过滤。例如:LOS_TraceEventMaskSet(0x202),则实际设置生效的mask为0x200(TRACE_QUE_FLAG),QUE模块的所有事件均会被采集。一般建议使用的方法为: LOS_TraceEventMaskSet(TRACE_EVENT_FLAG | TRACE_MUX_FLAG | TRACE_SEM_FLAG | TRACE_QUE_FLAG);

  • 如果仅需要使能简易插桩事件,通过设置Trace Mask为TRACE_MAX_FLAG即可。

  • Trace缓冲区有限,事件写满之后会覆盖写,用户可通过LOS_TraceRecordDump中打印的CurEvtIndex识别最新记录。

  • Trace的典型操作流程为:LOS_TraceStart、 LOS_TraceStop、 LOS_TraceRecordDump.

  • 针对中断事件的Trace, 提供中断号过滤,用于解决某些场景下特定中断号频繁触发导致其他事件被覆盖的情况,用户可自定义中断过滤的规则, 示例如下:

    BOOL Example_HwiNumFilter(UINT32 hwiNum)
    {
        if ((hwiNum == TIMER_INT) || (hwiNum == DMA_INT)) {
            return TRUE;
        }
        return FALSE;
    }
    LOS_TraceHwiFilterHookReg(Example_HwiNumFilter);
    c

则当中断号为TIMER_INT 或者DMA_INT时,不记录中断事件。

用户态

新增trace字符设备,位于"/dev/trace",通过对设备节点的read、write、ioctl,实现用户态trace的读写和控制:

  • read: 用户态读取Trace记录数据
  • write: 用户态事件写入
  • ioctl: 用户态Trace控制操作,包括
#define TRACE_IOC_MAGIC   'T'
#define TRACE_START      _IO(TRACE_IOC_MAGIC, 1)
#define TRACE_STOP       _IO(TRACE_IOC_MAGIC, 2)
#define TRACE_RESET      _IO(TRACE_IOC_MAGIC, 3)
#define TRACE_DUMP		 _IO(TRACE_IOC_MAGIC, 4)
#define TRACE_SET_MASK	 _IO(TRACE_IOC_MAGIC, 5)
c

分别对应Trace启动(LOS_TraceStart)、停止(LOS_TraceStop)、清除记录(LOS_TraceReset)、dump记录(LOS_TraceRecordDump)、设置事件过滤掩码(LOS_TraceEventMaskSet)

具体的使用方法参见 用户态编程实例。

开发指导

内核态开发流程

开启Trace调测的典型流程如下:

  1. 配置Trace模块相关宏。 配置Trace控制宏LOSCFG_KERNEL_TRACE,默认关,在 kernel/liteos_a 目录下执行 make update_config 命令配置 “Kernel->Enable Hook Feature->Enable Trace Feature” 中打开:
配置项menuconfig选项含义设置值
LOSCFG_KERNEL_TRACEEnable Trace FeatureTrace模块的裁剪开关YES/NO
LOSCFG_RECORDER_MODE_OFFLINETrace work mode ->Offline modeTrace工作模式为离线模式YES/NO
LOSCFG_RECORDER_MODE_ONLINETrace work mode ->Online modeTrace工作模式为在线模式YES/NO
LOSCFG_TRACE_CLIENT_INTERACTEnable Trace Client Visualization and Control使能与Trace IDE (dev tools)的交互,包括数据可视化和流程控制YES/NO
LOSCFG_TRACE_FRAME_CORE_MSGEnable Record more extended content
->Record cpuid, hardware interrupt status, task lock status
记录CPUID、中断状态、锁任务状态YES/NO
LOSCFG_TRACE_FRAME_EVENT_COUNTEnable Record more extended content
->Record event count,
which indicate the sequence of happend events
记录事件的次序编号YES/NO
LOSCFG_TRACE_FRAME_MAX_PARAMSRecord max params配置记录事件的最大参数个数INT
LOSCFG_TRACE_BUFFER_SIZETrace record buffer size配置Trace的缓冲区大小INT
  1. (可选)预置事件参数和事件桩(或使用系统默认的事件参数配置和事件桩)。

  2. (可选)调用LOS_TraceStop停止Trace后,清除缓冲区LOS_TraceReset(系统默认已启动trace)。

  3. (可选)调用LOS_TraceEventMaskSet设置需要追踪的事件掩码(系统默认的事件掩码仅使能中断与任务事件),事件掩码参见 los_trace.h 中的LOS_TRACE_MASK定义。

  4. 在需要记录事件的代码起始点调用LOS_TraceStart。

  5. 在需要记录事件的代码结束点调用LOS_TraceStop。

  6. 调用LOS_TraceRecordDump输出缓冲区数据(函数的入参为布尔型,FALSE表示格式化输出,TRUE表示输出到Trace IDE)。

上述第3-7步中的接口,均封装有对应的shell命令,开启shell后可执行相应的命令,对应关系如下:

  • LOS_TraceReset —— trace_reset
  • LOS_TraceEventMaskSet —— trace_mask
  • LOS_TraceStart —— trace_start
  • LOS_TraceStop —— trace_stop
  • LOS_TraceRecordDump —— trace_dump

内核态编程实例

本实例实现如下功能:

  1. 创建一个用于Trace测试的任务。
  2. 设置事件掩码。
  3. 启动trace。
  4. 停止trace。
  5. 格式化输出trace数据。

内核态示例代码

该示例代码的测试函数可以加在 kernel /liteos_a/testsuites /kernel /src /osTest.c 中的 TestTaskEntry 中进行测试。 实例代码如下:

#include "los_trace.h"
UINT32 g_traceTestTaskId;
VOID Example_Trace(VOID)
{
    UINT32 ret;
    LOS_TaskDelay(10);
    /* 开启trace */
    ret = LOS_TraceStart();
    if (ret != LOS_OK) {
        dprintf("trace start error\n");
        return;
    }
    /* 触发任务切换的事件 */
    LOS_TaskDelay(1);
    LOS_TaskDelay(1);
    LOS_TaskDelay(1);
    /* 停止trace */
    LOS_TraceStop();
    LOS_TraceRecordDump(FALSE);
}
UINT32 Example_Trace_test(VOID)
{
    UINT32 ret;
    TSK_INIT_PARAM_S traceTestTask;
    /* 创建用于trace测试的任务 */
    memset(&traceTestTask, 0, sizeof(TSK_INIT_PARAM_S));
    traceTestTask.pfnTaskEntry = (TSK_ENTRY_FUNC)Example_Trace;
    traceTestTask.pcName       = "TestTraceTsk";    /* 测试任务名称 */
    traceTestTask.uwStackSize  = 0x800; // 0x800: trace test task stacksize
    traceTestTask.usTaskPrio   = 5; // 5: trace test task priority
    traceTestTask.uwResved   = LOS_TASK_STATUS_DETACHED;
    ret = LOS_TaskCreate(&g_traceTestTaskId, &traceTestTask);
    if (ret != LOS_OK) {
        dprintf("TraceTestTask create failed .\n");
        return LOS_NOK;
    }
    /* 系统默认情况下已启动trace, 因此可先关闭trace后清除缓存区后,再重启trace */
    LOS_TraceStop();
    LOS_TraceReset();
    /* 开启任务模块事件记录 */
    LOS_TraceEventMaskSet(TRACE_TASK_FLAG);
    return LOS_OK;
}
LOS_MODULE_INIT(Example_Trace_test, LOS_INIT_LEVEL_KMOD_EXTENDED);
c

结果验证

输出结果如下:

***TraceInfo begin***
clockFreq = 50000000
CurEvtIndex = 7
Index   Time(cycles)      EventType      CurTask   Identity      params
0       0x366d5e88        0x45           0x1       0x0           0x1f         0x4       0x0
1       0x366d74ae        0x45           0x0       0x1           0x0          0x8       0x1f
2       0x36940da6        0x45           0x1       0xc           0x1f         0x4       0x9
3       0x3694337c        0x45           0xc       0x1           0x9          0x8       0x1f
4       0x36eea56e        0x45           0x1       0xc           0x1f         0x4       0x9
5       0x36eec810        0x45           0xc       0x1           0x9          0x8       0x1f
6       0x3706f804        0x45           0x1       0x0           0x1f         0x4       0x0
7       0x37070e59        0x45           0x0       0x1           0x0          0x8       0x1f
***TraceInfo end***

输出的事件信息包括:发生时间、事件类型、事件发生在哪个任务中、事件操作的主体对象、事件的其他参数。

  • EventType:表示的具体事件可查阅头文件 los_trace.h 中的enum LOS_TRACE_TYPE。

  • CurrentTask:表示当前运行在哪个任务中,值为taskid。

  • Identity:表示事件操作的主体对象,可查阅头文件 los_trace.h 中的#TYPE#_PARAMS。

  • params:表示的事件参数可查阅头文件 los_trace.h 中的#TYPE#_PARAMS。

下面以序号为0的输出项为例,进行说明。

Index   Time(cycles)      EventType      CurTask   Identity      params
0       0x366d5e88        0x45           0x1       0x0           0x1f         0x4
  • Time cycles可换算成时间,换算公式为cycles/clockFreq,单位为s。

  • 0x45为TASK_SWITCH即任务切换事件,当前运行的任务taskid为0x1。

  • Identity和params的含义需要查看TASK_SWITCH_PARAMS宏定义:

#define TASK_SWITCH_PARAMS(taskId, oldPriority, oldTaskStatus, newPriority, newTaskStatus) \
taskId, oldPriority, oldTaskStatus, newPriority, newTaskStatus
c

因为#TYPE#_PARAMS(IDENTITY, parma1…) IDENTITY, …,所以Identity为taskId(0x0),第一个参数为oldPriority(0x1f)

说明: params的个数由LOSCFG_TRACE_FRAME_MAX_PARAMS配置,默认为3,超出的参数不会被记录,用户应自行合理配置该值。

综上所述,任务由0x1切换到0x0,0x1任务的优先级为0x1f,状态为0x4,0x0任务的优先级为0x0。

经常有很多小伙伴抱怨说:不知道学习鸿蒙开发哪些技术?不知道需要重点掌握哪些鸿蒙应用开发知识点?

为了能够帮助到大家能够有规划的学习,这里特别整理了一套纯血版鸿蒙(HarmonyOS Next)全栈开发技术的学习路线,包含了鸿蒙开发必掌握的核心知识要点,内容有(ArkTS、ArkUI开发组件、Stage模型、多端部署、分布式应用开发、WebGL、元服务、OpenHarmony多媒体技术、Napi组件、OpenHarmony内核、OpenHarmony驱动开发、系统定制移植等等)鸿蒙(HarmonyOS NEXT)技术知识点。

在这里插入图片描述

《鸿蒙 (Harmony OS)开发学习手册》(共计892页):https://gitcode.com/HarmonyOS_MN/733GH/overview

如何快速入门?

1.基本概念
2.构建第一个ArkTS应用
3.……

开发基础知识:

1.应用基础知识
2.配置文件
3.应用数据管理
4.应用安全管理
5.应用隐私保护
6.三方应用调用管控机制
7.资源分类与访问
8.学习ArkTS语言
9.……

在这里插入图片描述

基于ArkTS 开发

1.Ability开发
2.UI开发
3.公共事件与通知
4.窗口管理
5.媒体
6.安全
7.网络与链接
8.电话服务
9.数据管理
10.后台任务(Background Task)管理
11.设备管理
12.设备使用信息统计
13.DFX
14.国际化开发
15.折叠屏系列
16.……

在这里插入图片描述

鸿蒙开发面试真题(含参考答案):https://gitcode.com/HarmonyOS_MN/733GH/overview

在这里插入图片描述

OpenHarmony 开发环境搭建

图片

《OpenHarmony源码解析》:https://gitcode.com/HarmonyOS_MN/733GH/overview

  • 搭建开发环境
  • Windows 开发环境的搭建
  • Ubuntu 开发环境搭建
  • Linux 与 Windows 之间的文件共享
  • ……
  • 系统架构分析
  • 构建子系统
  • 启动流程
  • 子系统
  • 分布式任务调度子系统
  • 分布式通信子系统
  • 驱动子系统
  • ……

图片

OpenHarmony 设备开发学习手册:https://gitcode.com/HarmonyOS_MN/733GH/overview

图片
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2159441.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

java项目之基于springboot框架开发的景区民宿预约系统的设计与实现(源码+文档)

项目简介 基于springboot框架开发的景区民宿预约系统的设计与实现的主要使用者分为: 管理员的功能有:用户信息的查询管理,可以删除用户信息、修改用户信息、新增用户信息,根据公告信息进行新增、修改、查询操作等等。。 &#x1…

英伟达NVIDIA数字IC后端笔试真题(ASIC Physical Design Engineer)

今天小编给大家分享下英伟达NVIDIA近两年数字IC后端笔试真题(ASIC Physical Design) 请使用OR门和INV反相器来搭建下面所示F逻辑表达式的电路图。 数字IC后端设计如何从零基础快速入门?(内附数字IC后端学习视频) 2024届IC秋招兆…

Vue3学习---【API】【从零开始的Vue3学习!!!】

目录 应用实例API app.mount() unmount() 常规API version nextTick() 状态选项API data() 注意: methods() 生命周期选项 beforeCreate()和Created() beforeCreate() created() beforeCreate()和created()的区别 beforeMount()和mounted() beforeM…

统信服务器操作系统【targetcli部署】

targetcli部署方案 文章目录 功能概述功能介绍1.安装targetcli2.targetcli语法及参数说明3.示例1. 配置2.访问功能概述 SCSI 即小型计算机系统接口(Small Computer System Interface;简写:SCSI) iSCSI,internet SCSI 网络磁盘 ,提供一对一的网络存储, 主机A 提供xx存储设…

数据包签名校验的Web安全测试实践

01 测试场景 在金融类的Web安全测试中,经常可以见到Web请求和响应数据包加密和签名保护,由于参数不可见,不能重放请求包,这类应用通常不能直接进行有效的安全测试,爬虫也爬不到数据。 02 解决思路 对于这类应用&am…

内存和管理

在 C 中,对象拷贝时编译器可能会进行一些优化,以提高程序的性能。 一种常见的优化是“返回值优化(Return Value Optimization,RVO)”和“具名返回值优化(Named Return Value Optimization,NRV…

记某学校小程序漏洞挖掘

前言: 遇到一个学校小程序的站点,只在前端登录口做了校验,后端没有任何校验,奇葩弱口令离谱进去,站点里面越权泄露敏感信息,接管账号等漏洞!!! 渗透思路 1.绕过前端 …

【学习笔记】TLS/SSL握手之Records

TLS / SSL会话是由记录(Records)所组成,有4种records HandshakeAlertChange Cipher SpecApplication DataHandshake和Alert Records被分为子类型(Subtypes): Handshake:Client HelloHandshake&a…

新手教学系列——Nginx静态文件访问优化,提升加载速度与用户体验

在构建现代Web应用时,静态文件的优化往往被初学者所忽略。静态文件,包括CSS样式、JavaScript脚本和图片等,是构建用户界面的关键元素。然而,随着应用规模的扩大,静态文件的数量和大小也随之增加,页面加载速度因此可能受到严重影响,进而影响用户体验。为了应对这种情况,…

01——springboot2基础知识

一、springboot的快速入门 springboot的作用:用来简化Spring应用的初始搭建以及开发过程 一、idea创建springboot工程——运行的步骤 选择Spring Initializr进行创建(现在基本上没有jdk1.8选了,都是jdk17了,需要的话&#xff0c…

使用四叉树碰撞的游戏 显微镜RPG

实现四叉树碰撞检测 //author bilibili 民用级脑的研发记录 // 开发环境 小熊猫c 2.25.1 raylib 版本 4.5 // 2024-7-14 // AABB 碰撞检测 在拖拽,绘制,放大缩小中 // 2024-7-20 // 直线改每帧打印一个点,生长的直线,直线炮弹 /…

Matplotlib-数据可视化详解

1. 数据可视化简介 可视化介绍 数据可视化是指直观展现数据,它是数据处理过程的一部分。 把数值绘制出来更方便比较。借助数据可视化,能更直观地理解数据,这是直接查看数据表做不到的 数据可视化有助于揭示数据中隐藏的模式,数据…

HDFS分布式文件系统01-HDFS架构与SHELL操作

HDFS分布式文件系统 学习目标第一课时知识点1-文件系统的分类单机文件系统网络文件系统分布式文件系统 知识点2-HDFS架构知识点3-HDFS的特点知识点4-HDFS的文件读写流程知识点5-HDFS的健壮性 第二课时知识点1-HDFS的Shell介绍HDFS Shell的语法格式如下。HDFS Shell客户端命令中…

三篇文章速通JavaSE到SpringBoot框架 上 JavaSE基础语法

文章目录 前置环境变量基本数据类型引用数据类型标识符运算符 流程控制三种基本流程结构 方法方法声明格式方法的调用方式方法的重载方法的重写重载和重写的区别 数组数组的特点 面向对象基本概念类的编写和对象的创建与使用类的编写对象的创建和使用 构造器构造器特点 封装以属…

55 循环神经网络RNN的实现_by《李沐:动手学深度学习v2》pytorch版

系列文章目录 文章目录 系列文章目录循环神经网络的从零开始实现[**独热编码**]初始化模型参数循环神经网络模型预测[**梯度裁剪**]训练小结练习 循环神经网络的从零开始实现 import math import torch from torch import nn from torch.nn import functional as F from d2l i…

玄机靶场--蚁剑流量

木马的连接密码是多少 黑客执行的第一个命令是什么 id 黑客读取了哪个文件的内容,提交文件绝对路径 /etc/passwd 黑客上传了什么文件到服务器,提交文件名 黑客上传的文件内容是什么 黑客下载了哪个文件,提交文件绝对路径 蚁剑流量特征总结 …

proteus仿真(2)

一,配置编译器 可以在proteus中写stm32的代码,需要先检查是否配置了keil的编译器 选择调试,编译器配置 stm32为ARM版本 51为8051版本 如果已经配置了keil—arm,但是打开没有,可以选择检查当前,刷新一下。 …

【有啥问啥】多臂老虎机(Multi-Armed Bandit,MAB)算法详解

多臂老虎机(Multi-Armed Bandit,MAB)算法详解 1. 引言 多臂老虎机(Multi-Armed Bandit,MAB)问题源自概率论和决策论,是一个经典的决策优化问题。最早提出的形式是赌场中的老虎机问题&#xff…

若依vue3.0表格的增删改查文件封装

一、因若依生成的文件没进行封装,维护起来比较麻烦。所以自己简单的进行封装了一下 gitee代码(文件)地址:https://gitee.com/liu_yu_ting09/ruo_yi.git 二、封装的方法(下面绿色按钮进行全局封装一个JeecgListMixin.js…

【解密 Kotlin 扩展函数】扩展函数的底层原理(十八)

导读大纲 1.1.1 从 Java 调用扩展函数1.1.2 扩展函数无法重载 1.1.1 从 Java 调用扩展函数 在编译器底层下,扩展函数是一种静态方法,它接受接收器对象作为第一个参数 调用它不涉及创建适配器对象或任何其他运行时开销这使得从 Java 使用扩展函数变得非常简单 调用静态方法并传…