Skip to content

Conversation

@caixr23
Copy link
Contributor

@caixr23 caixr23 commented Dec 19, 2025

Added signal handlers for common crash signals (SIGSEGV, SIGILL, SIGABRT, SIGFPE) to automatically clean up QML cache directory when crashes occur in release builds. This prevents corrupted cache from causing repeated startup failures. The handler first removes the qmlcache directory, then re-raises the signal to generate core dump for debugging.

Influence:

  1. Test application startup after simulating crashes to verify cache cleanup works
  2. Verify normal operation is unaffected in debug builds (QT_DEBUG defined)
  3. Check that core dumps are still generated for crash analysis
  4. Test QML loading performance after cache cleanup to ensure proper regeneration

feat: 添加崩溃处理及缓存清理功能

为常见崩溃信号(SIGSEGV、SIGILL、SIGABRT、SIGFPE)添加信号处理程序,在
发布版本中发生崩溃时自动清理QML缓存目录。这可以防止损坏的缓存导致重复启
动失败。处理程序会先删除qmlcache目录,然后重新触发信号以生成核心转储用于
调试。

Influence:

  1. 模拟崩溃后测试应用启动,验证缓存清理功能正常工作
  2. 验证调试版本(定义了QT_DEBUG)的正常操作不受影响
  3. 检查崩溃分析所需的核心转储是否仍然生成
  4. 测试缓存清理后的QML加载性能,确保正确重新生成

PMS: BUG-313293

Summary by Sourcery

New Features:

  • Introduce a crash handler for common fatal signals in release builds that removes the QML cache directory to avoid repeated startup failures caused by corrupted cache.

@deepin-ci-robot
Copy link

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by: caixr23

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

@deepin-ci-robot
Copy link

deepin pr auto review

我来对这个 diff 进行代码审查:

  1. 语法逻辑:
  • 代码语法正确,使用了标准的 C++ 信号处理机制
  • 信号处理函数 sig_crash 的实现逻辑合理,先清理缓存再重新触发信号
  1. 代码质量:
  • 优点:

    • 使用了条件编译 (#ifndef QT_DEBUG) 确保只在发布版本中启用信号处理
    • 信号处理函数实现简洁明了
    • 使用了标准库函数处理信号
  • 需要改进的地方:

    • 建议在 sig_crash 函数中添加日志输出,记录崩溃发生的时间
    • 可以考虑在清理缓存前检查缓存目录是否真的存在问题
    • 建议为信号处理函数添加注释说明每个信号的处理目的
  1. 代码性能:
  • 信号处理函数实现简单,性能影响较小
  • 使用 removeRecursively() 可能会消耗一些时间,但考虑到这是在崩溃时执行,影响不大
  1. 代码安全:
  • 优点:

    • 使用了 sigaction 而不是 signal,更安全可靠
    • 设置了 SA_RESETHAND 标志,避免信号处理函数的递归调用
  • 需要改进的地方:

    • 建议在信号处理函数中添加错误检查
    • 清理缓存操作应该有错误处理机制
    • 考虑添加权限检查,确保有足够权限删除缓存

改进建议:

#ifndef QT_DEBUG
void sig_crash(int signum)
{
    // 记录崩溃信息
    qCritical() << "Application crashed with signal:" << signum;
    
    // 崩溃了,尝试清下缓存,防止下次还起不来
    QString qmlCache = QStandardPaths::writableLocation(QStandardPaths::GenericCacheLocation) + "/deepin/dde-control-center/qmlcache";
    QDir dir(qmlCache);
    if (dir.exists()) {
        if (!dir.removeRecursively()) {
            qWarning() << "Failed to remove QML cache directory:" << qmlCache;
        }
    }
    
    // 重新触发信号,产生coredump
    signal(signum, SIG_DFL);
    raise(signum);
}
#endif

// 在 main 函数中添加错误检查
#ifndef QT_DEBUG
    // 设置信号处理函数
    struct sigaction sa;
    sa.sa_handler = sig_crash;
    sigemptyset(&sa.sa_mask);
    sa.sa_flags = SA_RESETHAND;

    // 检查信号处理函数设置是否成功
    if (sigaction(SIGSEGV, &sa, nullptr) == -1 ||
        sigaction(SIGILL, &sa, nullptr) == -1 ||
        sigaction(SIGABRT, &sa, nullptr) == -1 ||
        sigaction(SIGFPE, &sa, nullptr) == -1) {
        qWarning() << "Failed to set signal handlers";
    }
#endif

这些改进可以提高代码的健壮性和可维护性,同时保持原有的功能不变。

@sourcery-ai
Copy link

sourcery-ai bot commented Dec 19, 2025

Reviewer's Guide

Adds a crash signal handler in main.cpp (enabled only in non-debug builds) that deletes the QML cache directory on crash, then re-raises the original signal so a core dump is still produced, and wires it up using sigaction for several common fatal signals.

Sequence diagram for crash handling and QML cache cleanup

sequenceDiagram
    actor User
    participant App as dde_control_center
    participant OS as OperatingSystem
    participant Handler as sig_crash
    participant FS as FileSystem

    User->>App: Run application
    App->>OS: Execute
    OS-->>App: Runtime execution
    OS-->>App: Fatal signal (SIGSEGV/SIGILL/SIGABRT/SIGFPE)
    OS->>Handler: Invoke sig_crash(signum)
    Handler->>FS: Get GenericCacheLocation
    Handler->>FS: Remove qmlcache directory recursively
    Handler->>OS: signal(signum, SIG_DFL)
    Handler->>OS: raise(signum)
    OS-->>App: Deliver default signal handler
    OS-->>User: Application terminates and core dump is generated
Loading

Flow diagram for sig_crash handler logic

flowchart TD
    A[Receive fatal signal in release build] --> B[Enter sig_crash with signum]
    B --> C[Compute qmlCache path using QStandardPaths::GenericCacheLocation]
    C --> D[Create QDir for qmlcache directory]
    D --> E{Does qmlcache directory exist?}
    E -- Yes --> F[Call removeRecursively on qmlcache]
    E -- No --> G[Skip removal]
    F --> H["Set default handler for signum using signal(signum, SIG_DFL)"]
    G --> H["Set default handler for signum using signal(signum, SIG_DFL)"]
    H --> I["Call raise(signum) to re-deliver signal"]
    I --> J[Process receives default signal handler]
    J --> K[Application terminates and core dump is produced]
Loading

File-Level Changes

Change Details Files
Introduce a crash signal handler in release builds that clears the QML QML cache directory before re-raising the crash signal to allow core dump generation.
  • Include QDir and conditional signal.h headers needed for filesystem operations and signal handling in non-debug builds
  • Define a sig_crash handler (compiled only when QT_DEBUG is not defined) that locates the QML cache path via QStandardPaths, removes it recursively if present, then restores the default handler and re-raises the signal
  • In main(), install sig_crash via sigaction with SA_RESETHAND for SIGSEGV, SIGILL, SIGABRT, and SIGFPE so that these crashes trigger cache cleanup and then behave normally for debugging
src/dde-control-center/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

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:

  • The crash signal handler performs complex Qt and filesystem operations (QString, QDir, QStandardPaths, removeRecursively), which are not async-signal-safe and may lead to undefined behavior or deadlocks on crash; consider deferring cleanup to a safer mechanism (e.g., crash marker file checked on next startup) instead of doing it directly in the signal handler.
  • You’re both setting SA_RESETHAND on the sigaction and calling signal(signum, SIG_DFL) inside the handler; this is redundant and can be simplified by relying on SA_RESETHAND and just calling raise(signum) (or using sigaction without manipulating handlers inside the callback).
  • The struct sigaction sa is not zero-initialized, so any unused fields may contain garbage; it’s safer to use struct sigaction sa = {}; before assigning sa_handler, sa_mask, and sa_flags.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- The crash signal handler performs complex Qt and filesystem operations (QString, QDir, QStandardPaths, removeRecursively), which are not async-signal-safe and may lead to undefined behavior or deadlocks on crash; consider deferring cleanup to a safer mechanism (e.g., crash marker file checked on next startup) instead of doing it directly in the signal handler.
- You’re both setting SA_RESETHAND on the sigaction and calling signal(signum, SIG_DFL) inside the handler; this is redundant and can be simplified by relying on SA_RESETHAND and just calling raise(signum) (or using sigaction without manipulating handlers inside the callback).
- The struct sigaction sa is not zero-initialized, so any unused fields may contain garbage; it’s safer to use `struct sigaction sa = {};` before assigning sa_handler, sa_mask, and sa_flags.

## Individual Comments

### Comment 1
<location> `src/dde-control-center/main.cpp:30-39` </location>
<code_context>

+DCORE_USE_NAMESPACE
+#ifndef QT_DEBUG
+void sig_crash(int signum)
+{
+    // 崩溃了,尝试清下缓存,防止下次还起不来
+    QString qmlCache = QStandardPaths::writableLocation(QStandardPaths::GenericCacheLocation) + "/deepin/dde-control-center/qmlcache";
+    QDir dir(qmlCache);
+    if (dir.exists()) {
+        dir.removeRecursively();
+    }
+    // 重新触发信号,产生coredump
+    signal(signum, SIG_DFL);
+    raise(signum);
+}
+#endif
</code_context>

<issue_to_address>
**issue (bug_risk):** The signal handler uses non–async-signal-safe Qt/stdlib APIs, which is undefined behavior and may prevent the core dump or cache cleanup from working reliably.

In `sig_crash`, calls to `QStandardPaths::writableLocation`, `QString` operations, and `QDir::removeRecursively` are not async-signal-safe, so using them in a crash handler (SIGSEGV/SIGABRT, etc.) is undefined behavior and can deadlock or interfere with core dump generation.

To keep the behavior safe while still clearing the qmlcache:
- Limit the handler to async-signal-safe actions only (e.g., set a `sig_atomic_t` flag or write to a pipe).
- Move cache cleanup to a safe context, such as on next startup (check a crash marker file and then clear the cache) or in a separate helper process outside the crashing context.
</issue_to_address>

### Comment 2
<location> `src/dde-control-center/main.cpp:75-79` </location>
<code_context>
 {
+#ifndef QT_DEBUG
+    // 设置信号处理函数
+    struct sigaction sa;
+    sa.sa_handler = sig_crash;
+    sigemptyset(&sa.sa_mask);
+    // 在处理完信号后恢复默认信号处理
+    sa.sa_flags = SA_RESETHAND;
+
+    sigaction(SIGSEGV, &sa, nullptr);
</code_context>

<issue_to_address>
**suggestion (bug_risk):** The `sigaction` struct is only partially initialized; it’s safer to fully zero-initialize it before use.

On some platforms `struct sigaction` has extra fields (e.g., `sa_restorer`) that you’re not initializing here, leaving them with indeterminate values. A safer pattern is to zero-initialize first:
```cpp
struct sigaction sa;
memset(&sa, 0, sizeof(sa));
sa.sa_handler = sig_crash;
sigemptyset(&sa.sa_mask);
sa.sa_flags = SA_RESETHAND;
```
This guarantees all fields are in a defined state across platforms.

Suggested implementation:

```cpp
#ifndef QT_DEBUG
    // 设置信号处理函数
    struct sigaction sa;
    memset(&sa, 0, sizeof(sa));
    sa.sa_handler = sig_crash;
    sigemptyset(&sa.sa_mask);
    // 在处理完信号后恢复默认信号处理
    sa.sa_flags = SA_RESETHAND;

```

1. At the top of `src/dde-control-center/main.cpp`, ensure you have either `#include <cstring>` or `#include <string.h>` so that `memset` is declared.
</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.

Comment on lines +30 to +39
void sig_crash(int signum)
{
// 崩溃了,尝试清下缓存,防止下次还起不来
QString qmlCache = QStandardPaths::writableLocation(QStandardPaths::GenericCacheLocation) + "/deepin/dde-control-center/qmlcache";
QDir dir(qmlCache);
if (dir.exists()) {
dir.removeRecursively();
}
// 重新触发信号,产生coredump
signal(signum, SIG_DFL);
Copy link

Choose a reason for hiding this comment

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

issue (bug_risk): The signal handler uses non–async-signal-safe Qt/stdlib APIs, which is undefined behavior and may prevent the core dump or cache cleanup from working reliably.

In sig_crash, calls to QStandardPaths::writableLocation, QString operations, and QDir::removeRecursively are not async-signal-safe, so using them in a crash handler (SIGSEGV/SIGABRT, etc.) is undefined behavior and can deadlock or interfere with core dump generation.

To keep the behavior safe while still clearing the qmlcache:

  • Limit the handler to async-signal-safe actions only (e.g., set a sig_atomic_t flag or write to a pipe).
  • Move cache cleanup to a safe context, such as on next startup (check a crash marker file and then clear the cache) or in a separate helper process outside the crashing context.

Comment on lines +75 to +79
struct sigaction sa;
sa.sa_handler = sig_crash;
sigemptyset(&sa.sa_mask);
// 在处理完信号后恢复默认信号处理
sa.sa_flags = SA_RESETHAND;
Copy link

Choose a reason for hiding this comment

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

suggestion (bug_risk): The sigaction struct is only partially initialized; it’s safer to fully zero-initialize it before use.

On some platforms struct sigaction has extra fields (e.g., sa_restorer) that you’re not initializing here, leaving them with indeterminate values. A safer pattern is to zero-initialize first:

struct sigaction sa;
memset(&sa, 0, sizeof(sa));
sa.sa_handler = sig_crash;
sigemptyset(&sa.sa_mask);
sa.sa_flags = SA_RESETHAND;

This guarantees all fields are in a defined state across platforms.

Suggested implementation:

#ifndef QT_DEBUG
    // 设置信号处理函数
    struct sigaction sa;
    memset(&sa, 0, sizeof(sa));
    sa.sa_handler = sig_crash;
    sigemptyset(&sa.sa_mask);
    // 在处理完信号后恢复默认信号处理
    sa.sa_flags = SA_RESETHAND;
  1. At the top of src/dde-control-center/main.cpp, ensure you have either #include <cstring> or #include <string.h> so that memset is declared.

@caixr23 caixr23 requested a review from mhduiy December 19, 2025 05:47
Added signal handlers for common crash signals (SIGSEGV, SIGILL,
SIGABRT, SIGFPE) to automatically clean up QML cache directory when
crashes occur in release builds. This prevents corrupted cache from
causing repeated startup failures. The handler first removes the
qmlcache directory, then re-raises the signal to generate core dump
for debugging.

Influence:
1. Test application startup after simulating crashes to verify cache
cleanup works
2. Verify normal operation is unaffected in debug builds (QT_DEBUG
defined)
3. Check that core dumps are still generated for crash analysis
4. Test QML loading performance after cache cleanup to ensure proper
regeneration

feat: 添加崩溃处理及缓存清理功能

为常见崩溃信号(SIGSEGV、SIGILL、SIGABRT、SIGFPE)添加信号处理程序,在
发布版本中发生崩溃时自动清理QML缓存目录。这可以防止损坏的缓存导致重复启
动失败。处理程序会先删除qmlcache目录,然后重新触发信号以生成核心转储用于
调试。

Influence:
1. 模拟崩溃后测试应用启动,验证缓存清理功能正常工作
2. 验证调试版本(定义了QT_DEBUG)的正常操作不受影响
3. 检查崩溃分析所需的核心转储是否仍然生成
4. 测试缓存清理后的QML加载性能,确保正确重新生成

PMS: BUG-313293
@deepin-bot
Copy link

deepin-bot bot commented Dec 24, 2025

TAG Bot

New tag: 6.1.64
DISTRIBUTION: unstable
Suggest: synchronizing this PR through rebase #2902

@deepin-bot
Copy link

deepin-bot bot commented Dec 25, 2025

TAG Bot

New tag: 6.1.65
DISTRIBUTION: unstable
Suggest: synchronizing this PR through rebase #2906

@deepin-bot
Copy link

deepin-bot bot commented Jan 6, 2026

TAG Bot

New tag: 6.1.66
DISTRIBUTION: unstable
Suggest: synchronizing this PR through rebase #2927

@deepin-bot
Copy link

deepin-bot bot commented Jan 7, 2026

TAG Bot

New tag: 6.1.67
DISTRIBUTION: unstable
Suggest: synchronizing this PR through rebase #2928

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