查看源代码 cover (tools v4.1.1)

Erlang 的覆盖率分析工具

模块 cover 提供了一组用于 Erlang 程序覆盖率分析的函数,用于统计程序运行时每条可执行代码行被执行的次数。可执行行是指函数、casereceivetry 语句中的子句体中的代码行。子句头中的行、空行和仅包含注释的行不是可执行行。

覆盖率分析可用于验证测试用例是否覆盖了被测代码中的所有相关行。它在查找代码瓶颈时也很有帮助。

在进行任何分析之前,需要先对相关的模块进行覆盖率编译。这意味着在将模块编译成二进制文件并加载之前,会向模块添加一些额外的信息。模块的源文件不受影响,并且不会创建 .beam 文件。如果运行时系统本身支持覆盖率,Cover 将自动使用该功能来降低覆盖率编译代码的执行开销。

更改

Erlang/OTP 27 中添加了原生覆盖率支持。

每次调用覆盖率编译模块中的函数时,有关该调用的信息都会被添加到 Cover 的内部数据库中。覆盖率分析是通过检查 Cover 数据库的内容来执行的。输出 Answer 由两个参数确定:LevelAnalysis

  • Level = module

    Answer = {Module,Value},其中 Module 是模块名称。

  • Level = function

    Answer = [{Function,Value}],模块中每个函数一个元组。一个函数由它的模块名称 M、函数名称 F 和元数 A 指定为一个元组 {M,F,A}

  • Level = clause

    Answer = [{Clause,Value}],模块中每个子句一个元组。一个子句由它的模块名称 M、函数名称 F、元数 A 和在函数定义中的位置 C 指定为一个元组 {M,F,A,C}

  • Level = line

    Answer = [{Line,Value}],模块中每个可执行行一个元组。一行由它的模块名称 M 和源代码文件中的行号 N 指定为一个元组 {M,N}

  • Analysis = coverage

    Value = {Cov,NotCov},其中 Cov 是模块、函数、子句或行中至少被执行一次的可执行行的数量,NotCov 是未被执行的可执行行的数量。

  • Analysis = calls

    Value = Calls,它是模块、函数或子句被调用的次数。对于行级分析,Calls 是该行被执行的次数。

分布式

Cover 可用于分布式 Erlang 系统。系统中的一个节点必须被选为主节点,并且所有 Cover 命令都必须从该节点执行。如果在远程节点上调用接口函数,则会返回错误原因 not_main_node

使用 cover:start/1cover:stop/1 添加或删除节点。相同的覆盖率编译代码将加载到每个节点上,并且分析将收集并汇总来自所有节点的覆盖率数据结果。

要仅从远程节点收集数据而不停止这些节点上的 cover,请使用 cover:flush/1

如果到远程节点的连接中断,主节点会将其标记为丢失。如果该节点恢复,它将再次被添加。如果远程节点在断开连接期间处于活动状态,则此期间之前和期间的覆盖率数据将包含在分析中。

总结

函数

分析由 Arg 指定的一个或多个模块。

分析由 Arg1Arg2 指定的一个或多个模块。

通过检查内部数据库的内容,按照 AnalysisLevel 的指定,对一个或多个覆盖率编译的模块执行分析。

如果 Arg 是一个 analyse_option() 选项列表,则此调用等价于 analyse_to_file('_', Arg)

输出给定模块的源代码副本,并为每个可执行行注释执行计数。

此函数的工作方式与 analyse_to_file/2 相同,但它是异步的而不是同步的。

基于包含抽象代码(选项 debug_info)的 .beam 文件,对一个或多个模块进行覆盖率编译。

以与 compile_beam/1 相同的方式,对目录 Dir 中的所有 .beam 文件进行覆盖率编译。

以与 compile_module/1,2 相同的方式,编译目录 Dir 中的所有模块(.erl 文件)以进行 Cover 分析,并返回 Result 列表。

对一个或多个模块进行覆盖率编译。

Module 的当前覆盖率数据导出到文件 ExportFile

从远程节点上的 Cover 数据库获取数据,并将其存储在主节点上。

从使用 export/1,2 创建的文件 ExportFile 导入覆盖率数据。

返回所有导入文件的列表。

返回所有存在导入数据的模块的列表。

如果模块 Module 已覆盖率编译,则返回 {file, File},否则返回 false

仅支持在本地节点上运行 Cover。

返回当前已覆盖率编译的所有模块的列表。

重置所有节点上的 Cover 数据库中所有已覆盖率编译模块的所有覆盖率数据。

重置所有节点上的 Cover 数据库中已覆盖率编译模块 Module 的所有覆盖率数据。

启动拥有 Cover 内部数据库的 Cover 服务器。此函数由模块中的其他函数自动调用。

在每个给定节点上启动 Cover 服务器,并加载所有已覆盖率编译的模块。

停止 Cover 服务器并卸载所有已覆盖率编译的代码。

停止给定节点上的 Cover 服务器并卸载所有已覆盖率编译的代码。

返回作为覆盖率分析一部分的所有节点的列表。

类型

此类型的链接

analyse_answer()

查看源代码 (未导出)
-type analyse_answer() :: {ok, OutFile :: file:filename()} | {error, analyse_rsn()}.
此类型的链接

analyse_fail()

查看源代码 (未导出)
-type analyse_fail() :: [{not_cover_compiled, module()}].
此类型的链接

analyse_file_fail()

查看源代码 (未导出)
-type analyse_file_fail() :: [analyse_rsn()].
此类型的链接

analyse_file_ok()

查看源代码 (未导出)
-type analyse_file_ok() :: [OutFile :: file:filename()].
此类型的链接

analyse_item()

查看源代码 (未导出)
-type analyse_item() ::
          (Line :: {M :: module(), N :: non_neg_integer()}) |
          (Clause :: {M :: module(), F :: atom(), A :: arity(), C :: non_neg_integer()}) |
          (Function :: {M :: module(), F :: atom(), A :: arity()}).
此类型的链接

analyse_ok()

查看源代码 (未导出)
-type analyse_ok() ::
          [{Module :: module(), Value :: analyse_value()}] |
          [{Item :: analyse_item(), Value :: analyse_value()}].
此类型的链接

analyse_option()

查看源代码 (未导出)
-type analyse_option() ::
          html | {outfile, OutFile :: file:filename()} | {outdir, OutDir :: file:filename()}.
此类型的链接

analyse_rsn()

查看源代码 (未导出)
-type analyse_rsn() ::
          {not_cover_compiled, Module :: module()} |
          {file, File :: file:filename(), Reason :: term()} |
          {no_source_code_found, Module :: module()}.
此类型的链接

analyse_value()

查看源代码 (未导出)
-type analyse_value() ::
          {Cov :: non_neg_integer(), NotCov :: non_neg_integer()} | (Calls :: non_neg_integer()).
此类型的链接

analysis()

查看源代码 (未导出)
-type analysis() :: coverage | calls.
此类型的链接

beam_mod_file()

查看源代码 (未导出)
-type beam_mod_file() :: (Module :: module()) | (BeamFile :: file:filename()).
此类型的链接

beam_mod_files()

查看源代码 (未导出)
-type beam_mod_files() :: beam_mod_file() | [beam_mod_file()].
此类型的链接

compile_beam_result()

查看源代码 (未导出)
-type compile_beam_result() ::
          {ok, module()} | {error, BeamFile :: file:filename()} | {error, Reason :: compile_beam_rsn()}.
此类型的链接

compile_beam_rsn()

查看源代码 (未导出)
-type compile_beam_rsn() ::
          non_existing |
          {no_abstract_code, BeamFile :: file:filename()} |
          {encrypted_abstract_code, BeamFile :: file:filename()} |
          {already_cover_compiled, no_beam_found, module()} |
          {{missing_backend, module()}, BeamFile :: file:filename()} |
          {no_file_attribute, BeamFile :: file:filename()} |
          not_main_node.
此类型的链接

compile_result()

查看源代码 (未导出)
-type compile_result() :: {ok, Module :: module()} | {error, file:filename()} | {error, not_main_node}.
此类型的链接

export_reason()

查看源代码 (未导出)
-type export_reason() ::
          {not_cover_compiled, Module :: module()} |
          {cant_open_file, ExportFile :: file:filename(), FileReason :: term()} |
          not_main_node.
此类型的链接

file_error()

查看源代码 (未导出)
-type file_error() :: eacces | enoent.
-type level() :: line | clause | function | module.
此类型的链接

mod_file()

查看源代码 (未导出)
-type mod_file() :: (Module :: module()) | (File :: file:filename()).
此类型的链接

mod_files()

查看源代码 (未导出)
-type mod_files() :: mod_file() | [mod_file()].
此类型的链接

modules()

查看源代码 (未导出)
-type modules() :: module() | [module()].
此类型的链接

one_result()

查看源代码 (未导出)
-type one_result() ::
          {ok, {Module :: module(), Value :: analyse_value()}} |
          {ok, [{Item :: analyse_item(), Value :: analyse_value()}]} |
          {error, {not_cover_compiled, module()}}.
此类型的链接

option()

查看源代码 (未导出)
-type option() ::
          {i, Dir :: file:filename()} |
          {d, Macro :: atom()} |
          {d, Macro :: atom(), Value :: term()} |
          export_all.

函数

链接到此函数

analyse()

查看源代码 (自 OTP 18.0 起)
-spec analyse() -> {result, analyse_ok(), analyse_fail()} | {error, not_main_node}.

等价于 analyse('_', coverage, function)

-spec analyse(Analysis) -> {result, analyse_ok(), analyse_fail()} | {error, not_main_node}
                 when Analysis :: analysis();
             (Level) -> {result, analyse_ok(), analyse_fail()} | {error, not_main_node}
                 when Level :: level();
             (Modules) -> OneResult | {result, analyse_ok(), analyse_fail()} | {error, not_main_node}
                 when Modules :: modules(), OneResult :: one_result().

分析由 Arg 指定的一个或多个模块。

如果 Arganalysis() 中的一个值,则此调用等效于 analyse('_', Arg, function)

如果 Arglevel() 中的一个值,则此调用等效于 analyse('_', coverage, Arg)

否则,Arg 被假定为模块名,此调用等效于 analyse(Arg, coverage, function)

注意

要分析名称与 analysis()level() 中的值重叠的模块,模块名称必须在列表中。 例如,要分析名为 calls 的模块

cover:analyse([calls]).
-spec analyse(Analysis, Level) -> {result, analyse_ok(), analyse_fail()} | {error, not_main_node}
                 when Analysis :: analysis(), Level :: level();
             (Modules, Analysis) ->
                 OneResult | {result, analyse_ok(), analyse_fail()} | {error, not_main_node}
                 when Analysis :: analysis(), Modules :: modules(), OneResult :: one_result();
             (Modules, Level) ->
                 OneResult | {result, analyse_ok(), analyse_fail()} | {error, not_main_node}
                 when Level :: level(), Modules :: modules(), OneResult :: one_result().

分析由 Arg1Arg2 指定的一个或多个模块。

如果 Arg1analysis() 中的一个值,且 Arg2level() 中的一个值,则此调用等效于 analyse('_', Arg1, Arg2)

如果 Arg2analysis() 中的一个值,则假定 Arg1 是一个模块,并且此调用等效于 analyse(Arg1, Arg2, function)

如果 Arg2level() 中的一个值,则假定 Arg1 是一个模块,并且此调用等效于 analyse(Arg1, coverage, Arg2)

注意

要分析名称与 analysis() 中的值重叠的模块,模块名称需要放在列表中。例如,要分析名为 calls 的模块

cover:analyse([calls], function).
链接到此函数

analyse(Modules, Analysis, Level)

查看源代码
-spec analyse(Modules, Analysis, Level) ->
                 OneResult | {result, analyse_ok(), analyse_fail()} | {error, not_main_node}
                 when
                     Analysis :: analysis(),
                     Level :: level(),
                     Modules :: modules(),
                     OneResult :: one_result().

通过检查内部数据库的内容,按照 AnalysisLevel 的指定,对一个或多个覆盖率编译的模块执行分析。

如果 Modules 是一个原子(单个模块),则返回 OneResult,否则返回 {result, Ok, Fail}

如果 Modules 是原子 '_',则分析 cover 数据表中具有数据的所有模块。 请注意,这包括 cover 编译的模块和导入的模块。

如果给定的模块未进行 cover 编译,则由错误原因 {not_cover_compiled, Module} 指示。

链接到此函数

analyse_to_file()

查看源代码 (自 OTP 18.0 起)
-spec analyse_to_file() -> {result, analyse_file_ok(), analyse_file_fail()} | {error, not_main_node}.

等价于 analyse_to_file('_', [])

-spec analyse_to_file(Modules) ->
                         Answer |
                         {result, analyse_file_ok(), analyse_file_fail()} |
                         {error, not_main_node}
                         when Modules :: modules(), Answer :: analyse_answer();
                     (Options) ->
                         {result, analyse_file_ok(), analyse_file_fail()} | {error, not_main_node}
                         when Options :: [analyse_option()].

如果 Arg 是一个 analyse_option() 选项列表,则此调用等价于 analyse_to_file('_', Arg)

否则,Arg 被假定为模块,此调用等效于 analyse_to_file(Arg, [])

注意

要分析名称为 html 的模块(与 analyse_option() 中的一个选项重叠),则必须使用 cover:analyse_to_file/2

cover:analyse_to_file([html], []).
链接到此函数

analyse_to_file(Modules, Options)

查看源代码
-spec analyse_to_file(Modules, Options) ->
                         Answer |
                         {result, analyse_file_ok(), analyse_file_fail()} |
                         {error, not_main_node}
                         when
                             Modules :: modules(),
                             Options :: [analyse_option()],
                             Answer :: analyse_answer().

输出给定模块的源代码副本,并为每个可执行行注释执行计数。

输出文件 OutFile 默认为 Module.COVER.out,如果使用 html 选项,则默认为 Module.COVER.html

如果 Modules 是一个原子(一个模块),则返回 Answer,否则返回一个列表 {result, Ok, Fail}

如果 Modules 是 '_',则分析 Cover 数据表中具有数据的所有模块。 请注意,这包括 cover 编译的模块和导入的模块。

如果模块未进行 cover 编译,则由错误原因 {not_cover_compiled, Module} 指示。

如果无法使用 file:open/2 打开源文件和/或输出文件,则该函数返回 {error, {file, File, Reason}},其中 File 是文件名,Reason 是错误原因。

如果模块是从 .beam 文件进行 cover 编译的,即使用 compile_beam/1compile_beam_directory/0,1 ,则假定可以在以下位置之一找到源代码

  • .beam 文件相同的目录
  • 相对于具有 .beam 文件的目录的 ../src
  • Module:module_info(compile) 中的源路径,在这种情况下,将检查两个路径
    • 首先,通过连接 ../src 和编译路径的尾部(低于尾部的 src 组件)来构造的路径
    • 编译路径本身

如果找不到源代码,则由错误原因 {no_source_code_found, Module} 指示。

链接到此函数

async_analyse_to_file(Module)

查看源代码 (自 OTP R14B02 起)
-spec async_analyse_to_file(Module) -> pid() when Module :: module().

等价于 async_analyse_to_file/3

链接到此函数

async_analyse_to_file(Module, OutFileOrOpts)

查看源代码 (自 OTP R14B02 起)
-spec async_analyse_to_file(Module, OutFile) -> pid()
                               when Module :: module(), OutFile :: file:filename();
                           (Module, Options) -> pid()
                               when Module :: module(), Options :: [Option], Option :: html.

等价于 async_analyse_to_file/3

链接到此函数

async_analyse_to_file(Module, OutFile, Options)

查看源代码 (自 OTP R14B02 起)
-spec async_analyse_to_file(Module, OutFile, Options) -> pid()
                               when
                                   Module :: module(),
                                   OutFile :: file:filename(),
                                   Options :: [Option],
                                   Option :: html.

此函数的工作方式与 analyse_to_file/2 相同,但它是异步的而不是同步的。

创建时,生成的进程将与调用方链接。 如果在进行 cover 分析时发生 analyse_rsn() 类型的错误,该进程将崩溃,并使用与 analyse_to_file 返回的相同的错误原因。

-spec compile(ModFiles) -> Result | [Result] when ModFiles :: mod_files(), Result :: compile_result().

等价于 compile_module(ModFiles, [])

链接到此函数

compile(ModFiles, Options)

查看源代码
-spec compile(ModFiles, Options) -> Result | [Result]
                 when ModFiles :: mod_files(), Options :: [option()], Result :: compile_result().

等价于 compile_module(ModFile, Options)

链接到此函数

compile_beam(ModFiles)

查看源代码
-spec compile_beam(ModFiles) -> Result | [Result]
                      when ModFiles :: beam_mod_files(), Result :: compile_beam_result().

基于包含抽象代码(选项 debug_info)的 .beam 文件,对一个或多个模块进行覆盖率编译。

.beam 文件进行 Cover 编译比从源代码进行编译更快,也更方便,因为无需为包含路径或宏提供选项。 但是,现有的 .beam 文件必须使用选项 debug_info 进行编译,以便它们包含抽象代码

如果缺少抽象代码,则返回错误原因 {no_abstract_code, BeamFile}。 如果抽象代码已加密,并且没有可用于解密的密钥,则返回错误原因 {encrypted_abstract_code, BeamFile}

如果仅将模块名称(即,不是 .beam 文件的完整名称)提供给此函数,则通过调用 code:which(Module) 来查找 .beam 文件。 如果找不到 .beam 文件,则返回错误原因 non_existing。 如果该模块已使用 compile_beam/1 进行 cover 编译,则 .beam 文件将从首次编译时的相同位置选取。 如果该模块已使用 compile_module/2 进行 cover 编译,则无法找到正确的 .beam 文件,因此返回错误原因 {already_cover_compiled, no_beam_found, Module}

如果无法在节点上加载编译的代码,则返回 {error, BeamFile}

如果将 ModFiles 列表作为输入提供,则将返回 Result 列表。 返回列表的顺序是未定义的。

链接到此函数

compile_beam_directory()

查看源代码
-spec compile_beam_directory() -> [Result] | {error, Reason}
                                when Reason :: file_error(), Result :: compile_beam_result().

等价于 compile_beam_directory(".")

链接到此函数

compile_beam_directory(Dir)

查看源代码
-spec compile_beam_directory(Dir) -> [Result] | {error, Reason}
                                when
                                    Dir :: file:filename(),
                                    Reason :: file_error(),
                                    Result :: compile_beam_result().

以与 compile_beam/1 相同的方式,对目录 Dir 中的所有 .beam 文件进行覆盖率编译。

如果成功,此函数将返回 compile_beam_result() 列表。 否则,如果目录不可读,则返回 {error, eacces},如果该目录不存在,则返回 {error, enoent}

-spec compile_directory() -> [Result] | {error, Reason}
                           when Reason :: file_error(), Result :: compile_result().

等价于 compile_directory(".", [])

链接到此函数

compile_directory(Dir)

查看源代码
-spec compile_directory(Dir) -> [Result] | {error, Reason}
                           when
                               Dir :: file:filename(),
                               Reason :: file_error(),
                               Result :: compile_result().

等价于 compile_directory(Dir, [])

链接到此函数

compile_directory(Dir, Options)

查看源代码
-spec compile_directory(Dir, Options) -> [Result] | {error, Reason}
                           when
                               Dir :: file:filename(),
                               Options :: [option()],
                               Reason :: file_error(),
                               Result :: compile_result().

以与 compile_module/1,2 相同的方式,编译目录 Dir 中的所有模块(.erl 文件)以进行 Cover 分析,并返回 Result 列表。

如果目录不可读,此函数将返回 {error, eacces},如果该目录不存在,则返回 {error, enoent}

链接到此函数

compile_module(ModFiles)

查看源代码
-spec compile_module(ModFiles) -> Result | [Result]
                        when ModFiles :: mod_files(), Result :: compile_result().

等价于 compile_module(ModFile, [])

链接到此函数

compile_module(ModFiles, Options)

查看源代码
-spec compile_module(ModFiles, Options) -> Result | [Result]
                        when ModFiles :: mod_files(), Options :: [option()], Result :: compile_result().

对一个或多个模块进行覆盖率编译。

模块由其模块名称 Module 或其文件名 File 给出。

可以省略 .erl 扩展名。如果模块不在当前目录中,则必须指定其完整路径。

Options 是编译器选项的列表。只有定义包含文件目录和宏的选项会被传递给 compile:file/2;其他所有选项都会被忽略。

如果模块成功进行覆盖编译,该函数会返回 {ok, Module}。否则,该函数会返回 {error, File}。错误和警告会在发生时打印出来。

如果将 ModFiles 列表作为输入,则会返回一个 Result 列表。列表中返回结果的顺序是未定义的。

请注意,内部数据库在编译期间初始化,这意味着该模块之前收集的任何覆盖率数据都将丢失。

-spec export(File) -> ok | {error, Reason} when File :: file:filename(), Reason :: export_reason().

等价于 export(File, '_')

-spec export(File, Module) -> ok | {error, Reason}
                when File :: file:filename(), Module :: module(), Reason :: export_reason().

Module 的当前覆盖率数据导出到文件 ExportFile

建议使用 .coverdata 扩展名来命名 ExportFile

如果 Module 为 '_',则会导出所有已进行覆盖编译或较早导入的模块的数据。

如果要合并来自不同系统的覆盖率数据,此函数会很有用。

另请参见 import/1

链接到此函数

flush(Nodes)

查看源代码 (自 OTP R16B 起)
-spec flush(Nodes) -> ok | {error, not_main_node} when Nodes :: node() | [node()].

从远程节点上的 Cover 数据库获取数据,并将其存储在主节点上。

-spec import(ExportFile) -> ok | {error, Reason}
                when
                    ExportFile :: file:filename(),
                    Reason :: {cant_open_file, ExportFile, FileReason :: term()} | not_main_node.

从使用 export/1,2 创建的文件 ExportFile 导入覆盖率数据。

在此调用之后执行的任何分析都将包括导入的数据。

请注意,在编译模块时,所有现有的覆盖率数据都会被删除,包括导入的数据。如果导入数据时模块已经编译,则导入的数据会添加到现有的覆盖率数据中。

可以将来自多个导出文件的覆盖率数据导入到一个系统中。然后,在分析时会累加覆盖率数据。

除非首先重置或编译模块,否则不能从同一个文件导入同一个模块的覆盖率数据两次。该检查基于文件名,因此您可以通过重命名导出文件来轻松欺骗系统。

-spec imported() -> [file:filename()] | {error, not_main_node}.

返回所有导入文件的列表。

-spec imported_modules() -> [module()] | {error, not_main_node}.

返回所有存在导入数据的模块的列表。

-spec is_compiled(Module) -> {file, File :: file:filename()} | false | {error, not_main_node}
                     when Module :: module().

如果模块 Module 已覆盖率编译,则返回 {file, File},否则返回 false

Filecompile_module/1,2 使用的 .erl 文件,或者 compile_beam/1 使用的 .beam 文件。

链接到此函数

local_only()

查看源代码 (自 OTP 22.0 起)
-spec local_only() -> ok | {error, too_late}.

仅支持在本地节点上运行 Cover。

必须在编译任何模块或添加任何节点之前调用此函数。在此模式下运行时,模块将以更有效的方式进行覆盖编译,但生成的代码只能在编译它们的同一节点上工作。

-spec modules() -> [module()] | {error, not_main_node}.

返回当前已覆盖率编译的所有模块的列表。

-spec reset() -> ok | {error, not_main_node}.

重置所有节点上的 Cover 数据库中所有已覆盖率编译模块的所有覆盖率数据。

-spec reset(Module) -> ok | {error, not_main_node} | {error, {not_cover_compiled, Module}}
               when Module :: module().

重置所有节点上的 Cover 数据库中已覆盖率编译模块 Module 的所有覆盖率数据。

如果 Module 未进行覆盖编译,则该函数会返回 {error, {not_cover_compiled, Module}}

-spec start() -> {ok, pid()} | {error, Reason} when Reason :: {already_started, pid()} | term().

启动拥有 Cover 内部数据库的 Cover 服务器。此函数由模块中的其他函数自动调用。

-spec start(Nodes) -> {ok, StartedNodes} | {error, not_main_node} | {error, local_only}
               when Nodes :: node() | [node()], StartedNodes :: [node()].

在每个给定节点上启动 Cover 服务器,并加载所有已覆盖率编译的模块。

如果已调用 cover:local_only/0,则此调用将失败。

-spec stop() -> ok | {error, not_main_node}.

停止 Cover 服务器并卸载所有已覆盖率编译的代码。

-spec stop(Nodes) -> ok | {error, not_main_node} when Nodes :: node() | [node()].

停止给定节点上的 Cover 服务器并卸载所有已覆盖率编译的代码。

将从远程节点上的 Cover 数据库中提取数据并存储在主节点上。

-spec which_nodes() -> [node()].

返回作为覆盖率分析一部分的所有节点的列表。

请注意,不包括当前节点,因为它始终是分析的一部分。