Skip to content

feat: add ll-cli analyze size subcommand#1721

Merged
dengbo11 merged 1 commit into
OpenAtom-Linyaps:masterfrom
reddevillg:feat_size
Jul 6, 2026
Merged

feat: add ll-cli analyze size subcommand#1721
dengbo11 merged 1 commit into
OpenAtom-Linyaps:masterfrom
reddevillg:feat_size

Conversation

@reddevillg

Copy link
Copy Markdown
Collaborator

Add a new analyze size subcommand to ll-cli that reports on-disk usage of installed layers, breaking each module's footprint into:

  • exclusive: bytes unique to that module
  • shared: bytes shared with at least one other module
  • logical: exclusive + shared (apparent size to the module)
  • actual: shared bytes divided across the sharing modules

Sizes are computed from st_blocks (real block usage, not file size), and hard-linked inodes are deduplicated by (st_dev, st_ino) so they are counted once per sharing module. A separate pass reports the total real disk usage of the repository directory for cross-reference.

Options:
--sort {actual|logical|exclusive|shared|id} (default: actual)
--asc (default: descending)

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces the analyze size subcommand to the ll-cli utility, allowing users to view the disk space usage of installed modules (including exclusive, shared, logical, and actual sizes) and the repository's real size. The implementation adds CLI parsing, core size calculation logic, and both text and JSON output printers. The code review feedback highlights several important issues: performance bottlenecks caused by heap allocations during inode key string formatting, map overhead for non-hard-linked files, mathematical truncation errors during shared size division, lexicographical version sorting bugs, and column misalignment in the text printer when handling wide-character translations.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment thread libs/linglong/src/linglong/cli/cli.cpp
Comment thread libs/linglong/src/linglong/cli/cli.cpp
Comment thread libs/linglong/src/linglong/cli/cli.cpp Outdated
Comment thread libs/linglong/src/linglong/cli/cli_printer.cpp Outdated
@codecov

codecov Bot commented Jul 2, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 0% with 254 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
libs/linglong/src/linglong/cli/cli.cpp 0.00% 171 Missing ⚠️
libs/linglong/src/linglong/cli/cli_printer.cpp 0.00% 39 Missing ⚠️
libs/linglong/src/linglong/cli/json_printer.cpp 0.00% 22 Missing ⚠️
apps/ll-cli/src/main.cpp 0.00% 21 Missing ⚠️
libs/linglong/src/linglong/repo/ostree_repo.h 0.00% 1 Missing ⚠️
Files with missing lines Coverage Δ
libs/linglong/src/linglong/cli/cli.h 8.33% <ø> (ø)
libs/linglong/src/linglong/cli/cli_printer.h 100.00% <ø> (ø)
libs/linglong/src/linglong/cli/printer.h 66.66% <ø> (ø)
libs/linglong/src/linglong/repo/ostree_repo.h 8.33% <0.00%> (-0.76%) ⬇️
apps/ll-cli/src/main.cpp 0.00% <0.00%> (ø)
libs/linglong/src/linglong/cli/json_printer.cpp 0.00% <0.00%> (ø)
libs/linglong/src/linglong/cli/cli_printer.cpp 0.00% <0.00%> (ø)
libs/linglong/src/linglong/cli/cli.cpp 2.83% <0.00%> (-0.34%) ⬇️
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Add a new `analyze size` subcommand to ll-cli that reports on-disk usage
of installed layers, breaking each module's footprint into:

- exclusive: bytes unique to that module
- shared:    bytes shared with at least one other module
- logical:   exclusive + shared (apparent size to the module)
- actual:    shared bytes divided across the sharing modules

Sizes are computed from st_blocks (real block usage, not file size),
and hard-linked inodes are deduplicated by (st_dev, st_ino) so they
are counted once per sharing module. A separate pass reports the
total real disk usage of the repository directory for cross-reference.

Options:
  --sort {actual|logical|exclusive|shared|id}  (default: actual)
  --asc                                        (default: descending)

Signed-off-by: reddevillg <reddevillg@gmail.com>
@deepin-ci-robot

Copy link
Copy Markdown
Collaborator

deepin pr auto review

★ 总体评分:85分

■ 【总体评价】

代码实现了基于inode去重的模块磁盘占用分析功能,逻辑严密但存在代码重复与终端对齐缺陷
逻辑正确但因遍历逻辑重复和表头表体对齐方式不一致扣15分

■ 【详细分析】

  • 1.语法逻辑(基本正确)✓

核心计算逻辑calculateModuleSizes通过区分nlink == 1nlink > 1的情况,准确统计了模块的独占、共享和实际大小。calculateRealDiskUsage通过visitedInodes集合正确实现了跨硬链接的去重计算。Cli::size函数中通过reserve预分配内存,并安全地使用at访问容器,边界处理得当。
潜在问题:versionLess函数在版本号解析成功但相等时,会回退到字符串字典序比较,可能导致1.01.0.0排序不符合语义预期。
建议:在versionLess中,当解析成功且版本相等时,直接返回false而非依赖字符串比较,以保持版本比较的严格一致性。

  • 2.代码质量(一般)✕

calculateModuleSizescalculateRealDiskUsage中存在大段完全相同的文件系统递归遍历与错误处理代码,违反DRY原则。CLIPrinter::printModuleSizes中表头使用了adjustDisplayWidth处理多字节字符宽度,而数据行却直接使用std::setw,当中文字符出现时会导致列对齐错乱。此外printer.h中引入了未使用的<map>头文件。
潜在问题:重复代码增加后续维护成本;中文字符对齐错乱严重影响CLI输出美观度。
建议:将目录遍历逻辑提取为接受回调函数的公共模板方法;在数据行输出时同样调用adjustDisplayWidth计算填充宽度,或封装一个支持多字节字符对齐的输出流操作符;移除printer.h中多余的<map>头文件。

  • 3.代码性能(无性能问题)✓

使用了std::unordered_mapstd::unordered_set来记录和去重inode,保证了O(1)的平均查找与插入时间复杂度。在遍历前对moduleSizesmodulePaths调用了reserve,避免了 vector 在 push_back 时的多次内存重分配。文件系统遍历使用基于error_code的重载,避免了异常处理带来的额外开销。
建议:无需额外优化,当前实现已足够高效。

  • 4.代码安全(存在0个安全漏洞)✓

漏洞对比统计:新增漏洞 0 个,减少漏洞 0 个,持平 0 个
代码未引入任何安全漏洞。路径来源均通过内部接口getLayerDirgetRepoDir获取,不存在路径遍历风险。错误信息通过fmt::format安全拼接,不存在格式化字符串漏洞。系统调用lstat64直接使用C风格字符串,无注入风险。

  • 建议:保持现有的安全编码实践。

■ 【改进建议代码示例】

// 建议将重复的目录遍历逻辑提取为公共函数,放在匿名命名空间中
template<typename Func>
Result<void> traverseDirectory(const std::filesystem::path &dir, Func callback) noexcept
{
    LINGLONG_TRACE("traverse directory");
    std::error_code ec;

    auto rootResult = callback(dir);
    if (!rootResult) {
        return LINGLONG_ERR(rootResult);
    }

    auto iterator = std::filesystem::recursive_directory_iterator{
        dir,
        std::filesystem::directory_options::skip_permission_denied,
        ec
    };
    if (ec) {
        return LINGLONG_ERR(fmt::format("failed to open directory {}", dir), ec);
    }

    const auto end = std::filesystem::recursive_directory_iterator{};
    while (iterator != end) {
        const auto &entry = *iterator;
        auto result = callback(entry.path());
        if (!result) {
            return LINGLONG_ERR(result);
        }

        iterator.increment(ec);
        if (ec) {
            return LINGLONG_ERR(fmt::format("failed to iterate directory {}", dir), ec);
        }
    }

    return LINGLONG_OK;
}

// 在 CLIPrinter::printModuleSizes 中修复对齐问题示例
void CLIPrinter::printModuleSizes(const std::vector<ModuleSizeInfo> &list,
                                  std::uint64_t actualTotalSize,
                                  std::uint64_t repoSize)
{
    std::uint64_t totalExclusiveSize{ 0 };
    std::uint64_t totalSharedSize{ 0 };
    std::uint64_t totalLogicalSize{ 0 };

    std::cout << "\033[38;5;214m" << std::left << adjustDisplayWidth(qUtf8Printable(_("ID")), 43)
              << adjustDisplayWidth(qUtf8Printable(_("Version")), 16)
              << adjustDisplayWidth(qUtf8Printable(_("Channel")), 16)
              << adjustDisplayWidth(qUtf8Printable(_("Module")), 14)
              << adjustDisplayWidth(qUtf8Printable(_("Exclusive")), 14)
              << adjustDisplayWidth(qUtf8Printable(_("Shared")), 14)
              << adjustDisplayWidth(qUtf8Printable(_("Logical")), 14) << qUtf8Printable(_("Actual"))
              << "\033[0m" << std::endl;

    for (const auto &info : list) {
        totalExclusiveSize += info.exclusiveSize;
        totalSharedSize += info.sharedSize;
        totalLogicalSize += info.logicalSize;
        // 修复:使用 adjustDisplayWidth 代替 std::setw 以支持多字节中文字符对齐
        std::cout << adjustDisplayWidth(qUtf8Printable(info.id + " "), 43)
                  << adjustDisplayWidth(qUtf8Printable(info.version + " "), 16)
                  << adjustDisplayWidth(qUtf8Printable(info.channel + " "), 16)
                  << adjustDisplayWidth(qUtf8Printable(info.module + " "), 14)
                  << adjustDisplayWidth(qUtf8Printable(formatSize(info.exclusiveSize) + " "), 14)
                  << adjustDisplayWidth(qUtf8Printable(formatSize(info.sharedSize) + " "), 14)
                  << adjustDisplayWidth(qUtf8Printable(formatSize(info.logicalSize) + " "), 14)
                  << formatSize(info.actualSize) << std::endl;
    }

    std::cout << std::endl
              << _("Calculated logical total size: ") << formatSize(totalLogicalSize) << " ("
              << _("Exclusive: ") << formatSize(totalExclusiveSize) << ", " << _("Shared: ")
              << formatSize(totalSharedSize) << ")" << std::endl
              << _("Calculated actual total size: ") << formatSize(actualTotalSize) << std::endl
              << _("Repository real size: ") << formatSize(repoSize) << std::endl;
}

@reddevillg reddevillg requested a review from dengbo11 July 2, 2026 08:53
@deepin-ci-robot

Copy link
Copy Markdown
Collaborator

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by: dengbo11, reddevillg

The full list of commands accepted by this bot can be found here.

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@dengbo11 dengbo11 merged commit ab8574c into OpenAtom-Linyaps:master Jul 6, 2026
17 of 18 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants