Skip to content

feat(dock): add app launch event reporting for taskbar icons#1606

Open
Ivy233 wants to merge 1 commit into
linuxdeepin:masterfrom
Ivy233:fix/dde-am-eventlog
Open

feat(dock): add app launch event reporting for taskbar icons#1606
Ivy233 wants to merge 1 commit into
linuxdeepin:masterfrom
Ivy233:fix/dde-am-eventlog

Conversation

@Ivy233
Copy link
Copy Markdown
Contributor

@Ivy233 Ivy233 commented May 23, 2026

  1. Implement LaunchDurationReporter to report event 1000610003 when taskbar icons appear
  2. Report app metadata: name, launch type, version, unique ID, timestamp, and package type
  3. Query Application Manager via DBus for instance information and launch type
  4. Detect package type using ll-cli for linglong apps and dpkg-query for deb packages
  5. Cache linglong app versions with 10-minute TTL to handle version updates
  6. Use single-threaded worker pool for async DBus and process operations
  7. Thread-safe cache implementation with mutex protection

Log: Report app launch events when taskbar icons appear for analytics

Influence:

  1. Verify event 1000610003 triggers when new taskbar icons appear
  2. Verify correct version reporting for linglong and deb packages
  3. Verify cache refreshes after 10 minutes for updated app versions
  4. Verify no performance impact on icon appearance timing

feat(dock): 添加任务栏图标出现时的应用启动事件上报

  1. 实现 LaunchDurationReporter 在任务栏图标出现时上报 1000610003 事件
  2. 上报应用元数据:名称、启动类型、版本、唯一 ID、时间戳和包类型
  3. 通过 DBus 查询应用管理器获取实例信息和启动类型
  4. 使用 ll-cli 检测玲珑应用,使用 dpkg-query 检测 deb 包
  5. 缓存玲珑应用版本,10 分钟 TTL 处理版本更新
  6. 使用单线程工作池异步处理 DBus 和进程操作
  7. 使用互斥锁实现线程安全的缓存

Log: 任务栏图标出现时上报应用启动事件用于分析

Influence:

  1. 验证新图标出现时触发 1000610003 事件
  2. 验证玲珑和 deb 包版本正确上报
  3. 验证缓存在 10 分钟后刷新以获取更新的应用版本
  4. 验证对图标出现时机无性能影响

PMS: TASK-389405

Copy link
Copy Markdown

@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.

Sorry @Ivy233, you have reached your weekly rate limit of 500000 diff characters.

Please try again later or upgrade to continue using Sourcery

@deepin-ci-robot
Copy link
Copy Markdown

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by: Ivy233

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

@Ivy233 Ivy233 requested a review from BLumia May 23, 2026 06:07
@Ivy233 Ivy233 force-pushed the fix/dde-am-eventlog branch 2 times, most recently from dc49397 to 35405ee Compare May 23, 2026 07:10
1. Implement LaunchDurationReporter to report event 1000610003 when taskbar
icons appear
2. Report app metadata: name, launch type, version, unique ID, timestamp, and
package type
3. Query Application Manager via DBus for instance information
4. Detect package type using ll-cli for linglong apps and dpkg-query for deb
packages
5. Implement cache with 30-minute TTL for linglong and deb packages
6. Async D-Bus query execution for better performance

Log: Report app launch events when taskbar icons appear for analytics

Influence:
1. Verify event 1000610003 triggers when new taskbar icons appear
2. Verify correct version reporting for linglong and deb packages

feat(dock): 添加任务栏图标出现时的应用启动事件上报

1. 实现 LaunchDurationReporter 在任务栏图标出现时上报 1000610003 事件
2. 上报应用元数据:名称、启动类型、版本、唯一 ID、时间戳和包类型
3. 通过 DBus 查询应用管理器获取实例信息
4. 使用 ll-cli 检测玲珑应用,使用 dpkg-query 检测 deb 包
5. 实现 30 分钟 TTL 的玲珑和 deb 包缓存
6. D-Bus 查询异步执行,提升性能

Log: 任务栏图标出现时上报应用启动事件用于分析

Influence:
1. 验证新图标出现时触发 1000610003 事件
2. 验证玲珑和 deb 包版本正确上报

PMS: TASK-389405
@Ivy233 Ivy233 force-pushed the fix/dde-am-eventlog branch from 35405ee to 0b79013 Compare May 23, 2026 07:19
@deepin-ci-robot
Copy link
Copy Markdown

deepin pr auto review

这份代码实现了一个应用启动时长上报器,通过在后台线程查询 D-Bus 获取应用实例信息,结合本地缓存和命令行工具获取包版本,最终通过事件日志上报。整体架构思路清晰,但在线程安全、代码性能和代码健壮性方面存在一些需要重点关注的问题。

以下是详细的代码审查意见:

一、 语法与逻辑

  1. D-Bus 调用使用了阻塞 API

    • 问题:在 queryInstances 中使用了 appIface.call(...),这是一个同步阻塞调用。虽然你将其放入了 QThreadPool 的后台线程中执行,避免了阻塞主线程,但在高并发或 D-Bus 响应缓慢时,仍可能导致后台线程池(m_workerPool)被长时间挂起。
    • 建议:当前设定了 setTimeout(1000) 是个好习惯,但如果有条件,建议使用异步调用 QDBusInterface::callWithCallbackQDBusPendingCall 配合信号槽,以彻底避免线程阻塞。
  2. ll-cli 命令解析逻辑脆弱

    • 问题loadAllLinglongVersions 中通过空格分割行来解析 ll-cli list 的输出:lines[i].simplified().split(QLatin1Char(' '))。如果应用名称本身包含空格(虽然 Linux 应用名不常见,但某些本地化名称可能包含),这种解析方式会导致列错位。
    • 建议:检查 ll-cli 是否支持 JSON 格式输出(如 ll-cli list --json 或类似参数),如果有则优先解析 JSON。如果只能解析纯文本,建议使用正则表达式或更严格的分隔符逻辑。
  3. Deb 缓存过期后未清理

    • 问题:当 m_debCache 中的条目过期时,代码只是忽略了它并重新查询 dpkg-query,然后将新结果 insert 回去。但过期的旧条目并没有从 QHash 中移除,随着时间推移,m_debCache 可能会积累大量无效的过期键,造成轻微的内存泄漏。
    • 建议:在判断过期时,调用 m_debCache.remove(desktopId) 清理旧条目。

二、 代码性能

  1. ll-cli 缓存刷新可能导致任务排队阻塞

    • 问题m_workerPool 设置了 setMaxThreadCount(1),即所有上报任务串行执行。当 linglong 缓存过期(30分钟一次)时,第一个触发的任务会同步执行 loadAllLinglongVersions(),该函数内部会 proc.waitForFinished(3000),耗时至少 3 秒。在此期间,后续所有的上报任务都会被阻塞在队列中等待。
    • 建议
      • 方案 A:将 ll-cli 的加载放到独立的线程或使用 QProcess::startDetached 配合异步解析,避免阻塞工作线程。
      • 方案 B:在工作线程中先释放锁,单独执行 loadAllLinglongVersions(),执行完毕后再加锁更新缓存,避免在持锁期间执行耗时 3 秒的进程调用。
  2. 频繁调用 dpkg-query 进程

    • 问题:对于未安装的 deb 应用或查询失败的应用,每次都会启动一个新的 QProcess 去调用 dpkg-query,进程的创建和销毁开销很大。
    • 建议:对于查询失败的应用(如 pakType == "unknown"),也应该将其缓存起来(可以设置较短的 TTL,例如 5 分钟),避免短时间内重复拉起无效进程。

三、 代码安全

  1. 命令注入风险

    • 问题proc.start("dpkg-query", {"-W", "-f=${Version}", desktopId});proc.start("ll-cli", ...)。虽然 QProcess::start 的参数列表形式会自动处理参数转义,比 system() 函数安全得多,但 desktopId 来源于 D-Bus 传入的窗口信息。如果 desktopId 被恶意构造为包含特殊字符(如路径遍历字符),仍可能产生不可预期的行为。
    • 建议:在 reportWindowAppeared 入口处,对 desktopId 进行严格的格式校验(例如必须为 com.vendor.AppName 这种逆域名格式,且不包含 /..; 等特殊字符)。
  2. 版权年份错误

    • 问题SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd.
    • 建议:年份写成了 2026 年,应修正为当前年份或代码实际编写年份。

四、 代码质量与改进建议

  1. Q_UNUSED(future) 处理方式不佳

    • 问题QtConcurrent::run 返回的 QFuture 被标记为 Q_UNUSED。如果不关心返回值,可以直接忽略返回值;但如果需要防止 QFuture 析构导致任务取消(在某些 Qt 版本中),或者需要监控状态,当前写法不够明确。
    • 建议:如果只是为了避免编译器警告,可以直接不接收返回值:QtConcurrent::run(&m_workerPool, ...);(C++ 允许忽略返回值)。如果确实需要保留 future 以延长生命周期,建议加上注释说明原因。
  2. queryInstances 中的 D-Bus 转换逻辑可优化

    • 问题qdbus_cast<QList<QDBusObjectPath>>(reply.value()) 依赖 Qt 的全局 D-Bus 类型注册,如果未注册可能运行时出错。
    • 建议:确保在 globals.h 或项目初始化时已使用 qRegisterMetaType 注册了相关类型,或者使用更底层的 QDBusArgument 手动解析以确保健壮性。
  3. m_workerPool.waitForDone() 的阻塞风险

    • 问题:析构函数中 m_workerPool.waitForDone() 会阻塞当前线程直到任务完成。如果此时 D-Bus 请求卡死或 ll-cli 卡死,会导致所在对象(TaskManager)无法析构,进而导致应用退出时卡死。
    • 建议
      • waitForDone 设置一个合理的超时时间,例如 waitForDone(5000)
      • 在进入析构前,可以通过 m_workerPool.clear() 取消队列中未开始的任务。

改进后的核心代码示例(供参考)

针对上述部分问题,以下是修改后的 reportWindowAppeared 缓存逻辑片段:

void LaunchDurationReporter::reportWindowAppeared(const QString &desktopId)
{
    if (desktopId.isEmpty()) {
        return;
    }

    // 安全性:基础格式校验,防止命令注入或异常参数
    static QRegularExpression validIdRegex(R"(^[a-zA-Z0-9\.\-_]+$)");
    if (!validIdRegex.match(desktopId).hasMatch()) {
        qCWarning(launchDurationReporter) << "Invalid desktopId format:" << desktopId;
        return;
    }

    QtConcurrent::run(&m_workerPool, [this, desktopId]() {
        auto instances = queryInstances(desktopId);

        QString uniqueId;
        QString launchType = QStringLiteral("unknown");
        if (!instances.isEmpty()) {
            const auto &latest = instances.constLast();
            uniqueId = latest.instanceId;
            launchType = latest.launchType;
        }

        if (uniqueId.isEmpty()) {
            return;
        }

        QString version;
        QString pakType;

        {
            QMutexLocker locker(&m_cacheMutex);
            qint64 currentTime = QDateTime::currentSecsSinceEpoch();

            // 刷新 linglong 缓存
            if ((currentTime - m_linglongCacheTime) > kLinglongCacheTTLSeconds) {
                // 注意:这里仍在持锁状态调用 ll-cli 会阻塞其他线程
                // 如果对性能要求极高,应先 unlock() 执行 loadAllLinglongVersions(),完成后再 lock() 写入
                m_linglongCache = loadAllLinglongVersions(); 
                m_linglongCacheTime = currentTime;
            }

            if (m_linglongCache.contains(desktopId)) {
                version = m_linglongCache.value(desktopId);
                pakType = QStringLiteral("linglong");
            } else if (m_debCache.contains(desktopId)) {
                const auto &entry = m_debCache.value(desktopId);
                if ((currentTime - entry.timestamp) <= kDebCacheTTLSeconds) {
                    version = entry.version;
                    pakType = entry.pakType;
                } else {
                    // 改进:清理过期条目,防止内存泄漏
                    m_debCache.remove(desktopId); 
                }
            }
        }

        if (pakType.isEmpty()) {
            QProcess proc;
            proc.start(QStringLiteral("dpkg-query"), {QStringLiteral("-W"), QStringLiteral("-f=${Version}"), desktopId});
            proc.waitForFinished(1000);
            if (proc.exitCode() == 0) {
                version = QString::fromUtf8(proc.readAllStandardOutput()).trimmed();
                pakType = QStringLiteral("deb");
            } else {
                qCDebug(launchDurationReporter) << "dpkg-query failed for" << desktopId << "exitCode:" << proc.exitCode();
                // 改进:对于查询失败的也缓存,防止频繁拉起进程
                pakType = QStringLiteral("unknown");
            }

            QMutexLocker locker(&m_cacheMutex);
            // 将查询结果(包含 unknown)放入缓存
            m_debCache.insert(desktopId, {version, pakType, QDateTime::currentSecsSinceEpoch()});
        }

        QMetaObject::invokeMethod(this, [this, desktopId, uniqueId, launchType, version, pakType]() {
            doReport(desktopId, uniqueId, launchType, version, pakType);
        }, Qt::QueuedConnection);
    });
}

// 析构函数改进
LaunchDurationReporter::~LaunchDurationReporter()
{
    m_workerPool.clear(); // 先清空未执行的任务
    m_workerPool.waitForDone(3000); // 设置3秒超时,防止卡死
}

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.

2 participants