Skip to content

Conversation

@GongHeng2017
Copy link
Contributor

@GongHeng2017 GongHeng2017 commented Oct 16, 2025

-- Adjust code logic to show gpu mem.

Log: fix issue
Bug: https://pms.uniontech.com/bug-view-336355.html

Summary by Sourcery

Fix GPU memory display by implementing debugfs reading and parsing, refactor related GPU info retrieval methods, and clean up outdated command-based logic.

Bug Fixes:

  • Show GPU memory information by reading and parsing /sys/kernel/debug/gc/total_mem

Enhancements:

  • Introduce getGpuMemInfoForFTDTM to parse and convert GPU memory units
  • Refactor getGpuInfoForFTDTM and CommonTools::preGenerateGpuInfo to combine GPU base and memory info
  • Remove legacy custom command logic and DISPLAY/XAUTHORITY dependency for GPU info retrieval
  • Simplify DBus interface by replacing getGpuInfoByCustom with getGpuInfoForFTDTM and updating call signatures

-- Adjust code logic to show gpu mem.

Log: fix issue
Bug: https://pms.uniontech.com/bug-view-336355.html
@sourcery-ai
Copy link

sourcery-ai bot commented Oct 16, 2025

Reviewer's Guide

This PR replaces the legacy external GPU info commands with a direct sysfs reader for GPU memory, exposes it via a new D-Bus method, and integrates it into the existing GPU info flow while removing obsolete code.

Sequence diagram for GPU memory info retrieval via D-Bus

sequenceDiagram
    participant App
    participant CommonTools
    participant QDBusInterface
    participant DeviceInfoServer
    participant DeviceInterface
    App->>CommonTools: preGenerateGpuInfo()
    CommonTools->>QDBusInterface: call("getGpuInfoForFTDTM")
    QDBusInterface->>DeviceInfoServer: D-Bus call getGpuInfoForFTDTM
    DeviceInfoServer->>DeviceInterface: getGpuMemInfoForFTDTM(mapInfo)
    DeviceInterface->>DeviceInterface: Read /sys/kernel/debug/gc/total_mem
    DeviceInterface->>DeviceInterface: Parse and format memory info
    DeviceInterface-->>DeviceInfoServer: Return GPU memory info
    DeviceInfoServer-->>QDBusInterface: Return GPU memory info
    QDBusInterface-->>CommonTools: Return GPU memory info
    CommonTools-->>App: Return combined GPU info
Loading

Class diagram for updated DeviceInterface and related GPU info retrieval

classDiagram
    class DeviceInterface {
        +setMonitorDeviceFlag(bool flag)
        +getGpuInfoForFTDTM()
        -getUserAuthorPasswd()
        -getGpuMemInfoForFTDTM(QMap<QString, QString> &mapInfo)
    }
    class CommonTools {
        +preGenerateGpuInfo()
        -getGpuBaseInfo(QMap<QString, QString> &mapInfo)
    }
    class QDBusInterface {
        +call(method)
    }
    DeviceInterface <|-- DeviceInfoServer
    CommonTools --> QDBusInterface : uses
    QDBusInterface --> DeviceInfoServer : D-Bus call
    DeviceInfoServer --> DeviceInterface : calls
    CommonTools --> DeviceInterface : via D-Bus
Loading

File-Level Changes

Change Details Files
Introduce direct GPU memory reader and parser
  • Define kGraphicsMemory constant
  • Implement getGpuMemInfoForFTDTM to read /sys/kernel/debug/gc/total_mem
  • Parse memory value and unit via QRegularExpression
  • Convert units to GB and insert into map
deviceinterface.cpp
deviceinterface.h
Replace process-based GPU info retrieval with D-Bus method
  • Remove getGpuInfoByCustom and its QProcess logic
  • Add Q_SCRIPTABLE getGpuInfoForFTDTM that caches and formats GPU memory info
  • Update DBusInterface to remove custom GPU info call
deviceinterface.cpp
deviceinterface.h
DBusInterface.cpp
DBusInterface.h
Integrate GPU memory info into CommonTools preGenerateGpuInfo
  • Split static gpuBaseInfo and gpuMemInfo strings
  • Call new getGpuInfoForFTDTM D-Bus method for memory
  • Concatenate base info and memory info for return
commontools.cpp
Remove obsolete GPU info command path
  • Eliminate getGpuInfoCommandFromDConfig usage in CustomGenerator
  • Remove preGenerateGpuInfo call in main.cpp
  • Clean up related error logs and fallback logic
CustomGenerator.cpp
main.cpp

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@deepin-ci-robot
Copy link

deepin pr auto review

根据提供的代码差异,我来对代码进行审查,并提出改进意见:

  1. 代码逻辑方面:
  • 新增了getGpuMemInfoForFTDTM函数用于获取GPU内存信息,这是一个良好的改进,将功能模块化
  • 使用静态变量缓存GPU信息,避免重复读取,这是一个好的优化
  • 移除了getGpuInfoByCustom函数及其相关调用,简化了代码结构
  1. 代码质量方面:
  • 存在一些可以改进的地方:
    • 文件操作没有使用RAII方式,建议使用QTextStream来读取文件内容
    • 正则表达式可以预编译以提高性能
    • 单位转换的逻辑可以提取为单独的函数,提高代码可读性
    • 错误处理可以更加细化,区分不同类型的错误
  1. 代码性能方面:
  • 使用静态变量缓存GPU信息是好的性能优化
  • 但是文件读取部分可以进一步优化:
    • 可以考虑使用内存映射方式读取文件
    • 正则表达式匹配可以优化,避免不必要的回溯
    • 单位转换计算可以使用查表法代替多个if-else
  1. 代码安全方面:
  • 文件路径硬编码为"/sys/kernel/debug/gc/total_mem",需要考虑权限问题
  • 文件操作没有进行充分的错误检查
  • 正则表达式匹配结果没有进行边界检查
  • 需要添加对输入参数的验证

具体改进建议:

  1. 文件操作改进:
bool DeviceInterface::getGpuMemInfoForFTDTM(QMap<QString, QString> &mapInfo)
{
    const QString filePath = "/sys/kernel/debug/gc/total_mem";
    QFile file(filePath);
    
    if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
        qCritical() << "Error opening file:" << file.errorString();
        return false;
    }

    QTextStream in(&file);
    QString content = in.readAll();
    file.close();

    if (content.isEmpty()) {
        qCritical() << "Error: File is empty!";
        return false;
    }

    // 其余代码...
}
  1. 正则表达式优化:
static const QRegularExpression kMemInfoRegex(R"((\d+(?:\.\d+)?)\s*\(?(MB|GB|KB|B)\)?)",
    QRegularExpression::CaseInsensitiveOption);

bool DeviceInterface::getGpuMemInfoForFTDTM(QMap<QString, QString> &mapInfo)
{
    // ... 前面的文件操作代码 ...

    QRegularExpressionMatch match = kMemInfoRegex.match(content.trimmed());
    if (!match.hasMatch()) {
        qCritical() << "Error: Failed to parse memory info";
        return false;
    }

    // ... 其余代码 ...
}
  1. 单位转换优化:
QString DeviceInterface::convertToGB(double value, const QString &unit)
{
    static const QMap<QString, double> unitFactors = {
        {"B", 1.0 / (1024.0 * 1024.0 * 1024.0)},
        {"KB", 1.0 / (1024.0 * 1024.0)},
        {"MB", 1.0 / 1024.0},
        {"GB", 1.0}
    };

    auto it = unitFactors.find(unit.toUpper());
    if (it == unitFactors.end()) {
        qCritical() << "Error: Unsupported unit:" << unit;
        return QString();
    }

    return QString("%1GB").arg(value * it.value(), 0, 'f', 2);
}
  1. 安全性改进:
  • 添加文件权限检查
  • 添加输入验证
  • 添加异常处理
  1. 其他建议:
  • 添加详细的注释说明函数功能和参数
  • 考虑添加单元测试
  • 可以添加日志记录功能,便于问题追踪
  • 考虑使用配置文件管理硬编码的路径和常量

这些改进将提高代码的健壮性、可维护性和性能。同时,良好的错误处理和日志记录将有助于问题的快速定位和解决。

Copy link

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

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

Hey there - I've reviewed your changes - here's some feedback:

  • Add an explicit fallback or error branch in getGpuMemInfoForFTDTM for unrecognized units so totalValue doesn’t remain empty if the regex captures an unexpected unit.
  • Static caching of GPU info means it will never refresh at runtime; consider adding a cache invalidation or refresh trigger to pick up memory updates after the first call.
  • Both DeviceInterface and CommonTools contain similar loops to build the key:value strings from a QMap—extract that into a shared helper function to reduce code duplication.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- Add an explicit fallback or error branch in getGpuMemInfoForFTDTM for unrecognized units so totalValue doesn’t remain empty if the regex captures an unexpected unit.
- Static caching of GPU info means it will never refresh at runtime; consider adding a cache invalidation or refresh trigger to pick up memory updates after the first call.
- Both DeviceInterface and CommonTools contain similar loops to build the key:value strings from a QMap—extract that into a shared helper function to reduce code duplication.

## Individual Comments

### Comment 1
<location> `deepin-devicemanager-server/deepin-deviceinfo/src/loadinfo/deviceinterface.cpp:32` </location>
<code_context>
     return result == Authority::Yes;
 }

+bool DeviceInterface::getGpuMemInfoForFTDTM(QMap<QString, QString> &mapInfo)
+{
+    const QString filePath = "/sys/kernel/debug/gc/total_mem";
</code_context>

<issue_to_address>
**issue (complexity):** Consider refactoring the method by extracting helpers, using a unit conversion map, and simplifying formatting with join().

Here are a few low-risk refactorings you can apply to keep the exact same behaviour but break that monster method into bite-sized helpers, remove the deep if/else, and collapse your formatting loop into a single `join()`.

1) pull the file-read and empty-check out into its own helper:  
```cpp
static QString readFileContent(const QString& path) {
    QFile f(path);
    if (!f.open(QIODevice::ReadOnly|QIODevice::Text)) {
        qCritical() << "Error opening" << path << ":" << f.errorString();
        return {};
    }
    return QString::fromUtf8(f.readAll()).trimmed();
}
```

2) pull the regex match into a helper + make the regex `static const` so it isn’t reallocated every call:  
```cpp
namespace {
static const QRegularExpression kMemRx{
    R"((\d+(?:\.\d+)?)\s*\(?(MB|GB|KB|B)\)?)",
    QRegularExpression::CaseInsensitiveOption
};
}

bool parseMemInfo(const QString& content, double& outValue, QString& outUnit) {
    auto m = kMemRx.match(content);
    if (!m.hasMatch()) {
        qCritical() << "Error: no memory info in:" << content;
        return false;
    }
    outValue = m.captured(1).toDouble();
    outUnit  = m.captured(2).toUpper();
    return true;
}
```

3) replace the big `if/else` chain with a simple unit→factor map:  
```cpp
static const QHash<QString,double> kUnitToGb {
    { "B",  1.0/(1024*1024*1024) },
    { "KB", 1.0/(1024*1024)     },
    { "MB", 1.0/1024.0           },
    { "GB", 1.0                  }
};

QString convertToGbString(double value, const QString& unit) {
    double factor = kUnitToGb.value(unit, 0);
    if (factor == 0) {
        qCritical() << "Unknown unit:" << unit;
        return {};
    }
    return QString::number(value * factor, 'f', 2) + "GB";
}
```

4) re-write `getGpuMemInfoForFTDTM()` as a simple orchestrator, and replace your manual loop in `getGpuInfoForFTDTM()` with a `join()`:

```cpp
bool DeviceInterface::getGpuMemInfoForFTDTM(QMap<QString,QString>& mapInfo) {
    const QString c = readFileContent("/sys/kernel/debug/gc/total_mem");
    if (c.isEmpty()) return false;

    double value;
    QString unit;
    if (!parseMemInfo(c, value, unit)) return false;

    const QString totalGb = convertToGbString(value, unit);
    if (totalGb.isEmpty()) return false;

    mapInfo.insert(kGraphicsMemory, totalGb);
    return true;
}

QString DeviceInterface::getGpuInfoForFTDTM() {
    static const QString info = []{
        QMap<QString,QString> m;
        if (!DeviceInterface{}.getGpuMemInfoForFTDTM(m))
            return QString{};
        QStringList lines;
        for (auto it = m.constBegin(); it != m.constEnd(); ++it)
            lines << QStringLiteral("%1: %2").arg(it.key(), it.value());
        return lines.join(QLatin1Char('\n')) + QLatin1Char('\n');
    }();
    return info;
}
```

— now each helper does one job, the conversion is driven by a table instead of four branches, and your final formatting is a single `join()`.
</issue_to_address>

### Comment 2
<location> `deepin-devicemanager/src/Tool/commontools.cpp:269` </location>
<code_context>

 QString CommonTools::preGenerateGpuInfo()
 {
-    static QString gpuInfo { "" };
</code_context>

<issue_to_address>
**issue (complexity):** Consider refactoring preGenerateGpuInfo() by extracting caching and formatting logic into helper functions for clarity and maintainability.

Here’s one way to split retrieval/formatting out and collapse the nested logic in preGenerateGpuInfo():

```cpp
// helper to format a map into “key: value\n…”
static QString formatInfoMap(const QMap<QString,QString>& m) {
    QString s;
    for (auto it = m.begin(); it != m.end(); ++it)
        s += it.key() + ": " + it.value() + "\n";
    return s;
}

// caches/returns GPU‐base info
static QString getCachedGpuBaseInfo() {
    static QString base;
    if (base.isEmpty()) {
        QMap<QString,QString> m;
        if (!CommonTools::getGpuBaseInfo(m)) {
            qCritical() << "Error: failed to get gpu base info!";
            return {};
        }
        base = formatInfoMap(m);
    }
    return base;
}

// caches/returns GPU‐mem info
static QString getCachedGpuMemInfo() {
    static QString mem;
    if (mem.isEmpty()) {
        QDBusInterface iface("org.deepin.DeviceInfo",
                             "/org/deepin/DeviceInfo",
                             "org.deepin.DeviceInfo",
                             QDBusConnection::systemBus());
        if (!iface.isValid()) {
            qCritical() << "Error: GPU mem DBus interface invalid";
        } else {
            auto reply = iface.call("getGpuInfoForFTDTM");
            if (reply.isValid()) mem = reply.value();
            else                qCritical() << "Error: failed to call getGpuInfoForFTDTM";
        }
    }
    return mem;
}

// simplified preGenerateGpuInfo()
QString CommonTools::preGenerateGpuInfo() {
    auto base = getCachedGpuBaseInfo();
    if (base.isEmpty()) return {};  // already logged
    return base + getCachedGpuMemInfo();
}
```

– Moves string‐building into formatInfoMap  
– Moves each cache into its own function  
– Makes preGenerateGpuInfo() a straight-line composition of those two calls
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

return result == Authority::Yes;
}

bool DeviceInterface::getGpuMemInfoForFTDTM(QMap<QString, QString> &mapInfo)
Copy link

Choose a reason for hiding this comment

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

issue (complexity): Consider refactoring the method by extracting helpers, using a unit conversion map, and simplifying formatting with join().

Here are a few low-risk refactorings you can apply to keep the exact same behaviour but break that monster method into bite-sized helpers, remove the deep if/else, and collapse your formatting loop into a single join().

  1. pull the file-read and empty-check out into its own helper:
static QString readFileContent(const QString& path) {
    QFile f(path);
    if (!f.open(QIODevice::ReadOnly|QIODevice::Text)) {
        qCritical() << "Error opening" << path << ":" << f.errorString();
        return {};
    }
    return QString::fromUtf8(f.readAll()).trimmed();
}
  1. pull the regex match into a helper + make the regex static const so it isn’t reallocated every call:
namespace {
static const QRegularExpression kMemRx{
    R"((\d+(?:\.\d+)?)\s*\(?(MB|GB|KB|B)\)?)",
    QRegularExpression::CaseInsensitiveOption
};
}

bool parseMemInfo(const QString& content, double& outValue, QString& outUnit) {
    auto m = kMemRx.match(content);
    if (!m.hasMatch()) {
        qCritical() << "Error: no memory info in:" << content;
        return false;
    }
    outValue = m.captured(1).toDouble();
    outUnit  = m.captured(2).toUpper();
    return true;
}
  1. replace the big if/else chain with a simple unit→factor map:
static const QHash<QString,double> kUnitToGb {
    { "B",  1.0/(1024*1024*1024) },
    { "KB", 1.0/(1024*1024)     },
    { "MB", 1.0/1024.0           },
    { "GB", 1.0                  }
};

QString convertToGbString(double value, const QString& unit) {
    double factor = kUnitToGb.value(unit, 0);
    if (factor == 0) {
        qCritical() << "Unknown unit:" << unit;
        return {};
    }
    return QString::number(value * factor, 'f', 2) + "GB";
}
  1. re-write getGpuMemInfoForFTDTM() as a simple orchestrator, and replace your manual loop in getGpuInfoForFTDTM() with a join():
bool DeviceInterface::getGpuMemInfoForFTDTM(QMap<QString,QString>& mapInfo) {
    const QString c = readFileContent("/sys/kernel/debug/gc/total_mem");
    if (c.isEmpty()) return false;

    double value;
    QString unit;
    if (!parseMemInfo(c, value, unit)) return false;

    const QString totalGb = convertToGbString(value, unit);
    if (totalGb.isEmpty()) return false;

    mapInfo.insert(kGraphicsMemory, totalGb);
    return true;
}

QString DeviceInterface::getGpuInfoForFTDTM() {
    static const QString info = []{
        QMap<QString,QString> m;
        if (!DeviceInterface{}.getGpuMemInfoForFTDTM(m))
            return QString{};
        QStringList lines;
        for (auto it = m.constBegin(); it != m.constEnd(); ++it)
            lines << QStringLiteral("%1: %2").arg(it.key(), it.value());
        return lines.join(QLatin1Char('\n')) + QLatin1Char('\n');
    }();
    return info;
}

— now each helper does one job, the conversion is driven by a table instead of four branches, and your final formatting is a single join().

return cmd;
}

QString CommonTools::preGenerateGpuInfo()
Copy link

Choose a reason for hiding this comment

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

issue (complexity): Consider refactoring preGenerateGpuInfo() by extracting caching and formatting logic into helper functions for clarity and maintainability.

Here’s one way to split retrieval/formatting out and collapse the nested logic in preGenerateGpuInfo():

// helper to format a map into “key: value\n…”
static QString formatInfoMap(const QMap<QString,QString>& m) {
    QString s;
    for (auto it = m.begin(); it != m.end(); ++it)
        s += it.key() + ": " + it.value() + "\n";
    return s;
}

// caches/returns GPU‐base info
static QString getCachedGpuBaseInfo() {
    static QString base;
    if (base.isEmpty()) {
        QMap<QString,QString> m;
        if (!CommonTools::getGpuBaseInfo(m)) {
            qCritical() << "Error: failed to get gpu base info!";
            return {};
        }
        base = formatInfoMap(m);
    }
    return base;
}

// caches/returns GPU‐mem info
static QString getCachedGpuMemInfo() {
    static QString mem;
    if (mem.isEmpty()) {
        QDBusInterface iface("org.deepin.DeviceInfo",
                             "/org/deepin/DeviceInfo",
                             "org.deepin.DeviceInfo",
                             QDBusConnection::systemBus());
        if (!iface.isValid()) {
            qCritical() << "Error: GPU mem DBus interface invalid";
        } else {
            auto reply = iface.call("getGpuInfoForFTDTM");
            if (reply.isValid()) mem = reply.value();
            else                qCritical() << "Error: failed to call getGpuInfoForFTDTM";
        }
    }
    return mem;
}

// simplified preGenerateGpuInfo()
QString CommonTools::preGenerateGpuInfo() {
    auto base = getCachedGpuBaseInfo();
    if (base.isEmpty()) return {};  // already logged
    return base + getCachedGpuMemInfo();
}

– Moves string‐building into formatInfoMap
– Moves each cache into its own function
– Makes preGenerateGpuInfo() a straight-line composition of those two calls

@deepin-ci-robot
Copy link

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by: GongHeng2017, max-lvs

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

@GongHeng2017
Copy link
Contributor Author

/forcemerge

@deepin-bot
Copy link
Contributor

deepin-bot bot commented Oct 16, 2025

This pr force merged! (status: unstable)

@deepin-bot deepin-bot bot merged commit ee4e916 into linuxdeepin:develop/eagle Oct 16, 2025
16 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