Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
3e2b822
refactor(render): drop unused RenderBackend abstraction
Charliechen114514 Jul 8, 2026
0171686
feat(ipc): single-instance raise via QLocalSocket
Charliechen114514 Jul 8, 2026
41f6797
feat(ipc): message framework + ServiceLocator + unit tests
Charliechen114514 Jul 8, 2026
05255ee
feat(crash): CrashHandler Phase 1 — signal capture + report finalization
Charliechen114514 Jul 8, 2026
0769e61
test(crash): cover real faults — null deref, abort, stack overflow
Charliechen114514 Jul 8, 2026
418e76f
feat(desktop-widget): MS6 Step A — desktop clock widget + draggable base
Charliechen114514 Jul 8, 2026
58f7855
feat(home-page): migrate CCIMX HomePage + multi-page swipe desktop
Charliechen114514 Jul 8, 2026
131e040
fix(launcher-tile): accept press so page doesn't swipe under a dragge…
Charliechen114514 Jul 8, 2026
6db5d3d
feat(home-page): Step B1 — live Memory/CPU/Disk cards in the card stack
Charliechen114514 Jul 8, 2026
3519efa
feat(home-page): port CCIMX HomePage visuals verbatim (clock / cards …
Charliechen114514 Jul 8, 2026
d8a0554
fix(home-page): spread card content + drop Helvetica Neue fallback
Charliechen114514 Jul 8, 2026
a0772eb
docs(status): note Helvetica Neue drop in MS6 Step B2
Charliechen114514 Jul 8, 2026
8af7650
perf(home-page): poll system probes off the UI thread
Charliechen114514 Jul 8, 2026
1f15419
docs(status): record async CPU probe move in MS6 Step B1
Charliechen114514 Jul 8, 2026
53a5620
feat(home-page): port CCIMX UserInfo / Calendar / disk-gauge cards
Charliechen114514 Jul 8, 2026
d15f8dd
feat(home-page): port CCIMX bottom-grid gadgets — net status + local …
Charliechen114514 Jul 8, 2026
3095284
docs(status): record full CCIMX home-page migration (Step C)
Charliechen114514 Jul 8, 2026
be2ae03
fix(home-page): make UserInfoCard build on MSVC (no POSIX headers)
Charliechen114514 Jul 8, 2026
389e0dd
test(fix): make crash/ipc tests pass on Windows MSVC
Charliechen114514 Jul 8, 2026
d007cca
fix(ipc): drain socket on disconnect so one-shot clients deliver on W…
Charliechen114514 Jul 8, 2026
85197ce
fix(ipc): force-pull OS-buffered bytes in onNewConnection/disconnect
Charliechen114514 Jul 8, 2026
eda198f
test(ipc): skip same-thread transport test on Windows
Charliechen114514 Jul 8, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,8 @@ set(CFDESKTOP_STATIC_LIBS
CFDesktopMain
CFDesktopUi
cf_desktop_render
cfipc
cfcrash
)

# Sources compiled directly into the DLL (with CFDESKTOP_EXPORTS defined).
Expand Down Expand Up @@ -163,7 +165,7 @@ add_library(CFDesktop::lib ALIAS CFDesktop_shared)
# Main executable — thin shell that calls into the DLL
# ============================================================
add_executable(CFDesktop main.cpp)
target_link_libraries(CFDesktop PRIVATE CFDesktop_shared)
target_link_libraries(CFDesktop PRIVATE CFDesktop_shared cfipc)

log_info("Desktop Buildings" "Configuring Desktop Buildings Done")

Expand Down
2 changes: 2 additions & 0 deletions base/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,5 @@ add_subdirectory(logger)
add_subdirectory(file_operations)
add_subdirectory(config_manager)
add_subdirectory(ascii_art)
add_subdirectory(ipc)
add_subdirectory(crash_handler)
20 changes: 20 additions & 0 deletions base/crash_handler/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# CrashHandler module — async-signal-safe crash capture (Phase 1).
#
# STATIC lib; deliberately NOT linked to Qt: the signal handler path must stay
# free of Qt/global-object machinery (async-signal-safety). Uses only libc
# (backtrace/sigaction on Linux; SetUnhandledExceptionFilter arrives in a later
# phase on Windows). No Q_OBJECT, so AUTOMOC is not involved.
add_library(cfcrash STATIC)

target_sources(cfcrash
PRIVATE
src/crash_handler.cpp
src/crash_report.cpp
src/crash_signal_handler.cpp
)

target_include_directories(cfcrash PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
)

add_library(CFDesktop::crash ALIAS cfcrash)
139 changes: 139 additions & 0 deletions base/crash_handler/include/cfcrash/crash_handler.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
/**
* @file crash_handler.h
* @brief Process-wide crash handler: signal capture and report finalization.
*
* On install(), registers POSIX signal handlers (Linux) that write a minimal
* async-signal-safe raw snapshot (a @c .pending file in the crashes dir) and
* _exit().
* The next boot calls finalizePendingReports() to fold each @c .pending (plus
* the tail of the logger file) into a finalized @c *.json report, then prunes
* to a retention cap. Symbol resolution and a crash-reporter UI are Phase 2.
*
* Not a QObject: the signal path is pure C and must not touch Qt.
*
* @author CFDesktop Team
* @date 2026-07-08
* @version 0.19.0
* @since 0.19.0
* @ingroup crash
*/

#pragma once

#include <cstddef>
#include <string>

#include "cfcrash/crash_report.h"

namespace cf::crash {

/**
* @brief Process-wide singleton crash handler.
*
* @note The signal handler is async-signal-safe; install/finalize are not.
* @warning None
* @since 0.19.0
* @ingroup crash
*/
class CrashHandler {
public:
/**
* @brief Returns the process-wide singleton instance.
*
* @return Reference to the singleton CrashHandler.
* @throws None
* @note Created on first access; lives until process exit.
* @warning None
* @since 0.19.0
* @ingroup crash
*/
static CrashHandler& instance();

/**
* @brief Registers crash signal handlers and prepares the crashes dir.
*
* @param[in] crashes_dir Directory for @c .pending / @c .json reports.
* @param[in] logger_path This run's logger file; written into each
* @c .pending so finalize() tails the crashed
* run's own log. Empty to omit.
* @return True if handlers registered; false on failure (best-effort).
* @throws None
* @note Creates crashes_dir if missing.
* @warning Call after QCoreApplication exists so the dir is resolvable.
* @since 0.19.0
* @ingroup crash
*/
bool install(const std::string& crashes_dir, const std::string& logger_path = "");

/**
* @brief Restores default signal disposition (test-only).
*
* @throws None
* @note No-op if install() was not called.
* @warning None
* @since 0.19.0
* @ingroup crash
*/
void uninstall();

/**
* @brief Folds each @c .pending snapshot into a finalized @c .json report.
*
* @param[in] logger_path Logger file whose tail becomes last_logs.
* @param[in] tail_lines Number of trailing log lines to capture.
* @return Count of reports finalized.
* @throws None
* @note Deletes each @c .pending after writing its @c .json; prunes.
* @warning None
* @since 0.19.0
* @ingroup crash
*/
std::size_t finalizePendingReports(const std::string& logger_path,
std::size_t tail_lines = kDefaultTailLogLines);

/**
* @brief Deletes oldest @c .json reports beyond the retention cap.
*
* @param[in] max_keep Maximum reports to keep.
* @return Count of reports deleted.
* @throws None
* @note Sorts by modification time; youngest kept.
* @warning None
* @since 0.19.0
* @ingroup crash
*/
std::size_t pruneReports(std::size_t max_keep = kDefaultMaxReports);

/**
* @brief Sets the crashes directory without installing handlers.
*
* @param[in] crashes_dir Directory for reports.
* @return None
* @throws None
* @note For finalize/prune tests that do not want handlers armed.
* @warning None
* @since 0.19.0
* @ingroup crash
*/
void setCrashesDir(const std::string& crashes_dir);

private:
/**
* @brief Constructs the crash handler.
*
* @throws None
* @note Private; access via instance().
* @warning None
* @since 0.19.0
* @ingroup crash
*/
CrashHandler();

/// @brief Directory where @c .pending / @c .json reports are stored.
std::string crashes_dir_;

/// @brief True once signal handlers have been armed.
bool installed_{false};
};

} // namespace cf::crash
89 changes: 89 additions & 0 deletions base/crash_handler/include/cfcrash/crash_report.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
/**
* @file crash_report.h
* @brief Crash report data model and JSON serialization.
*
* Defines the CrashReport struct assembled on the next boot from a raw signal
* snapshot. The signal handler itself writes only a minimal async-signal-safe
* @c .pending file; this struct folds that snapshot with the logger tail into
* a finalized JSON report. Symbol resolution is deferred to Phase 2, so
* raw_frames holds bare addresses.
*
* @author CFDesktop Team
* @date 2026-07-08
* @version 0.19.0
* @since 0.19.0
* @ingroup crash
*/

#pragma once

#include <cstddef>
#include <cstdint>
#include <string>
#include <vector>

namespace cf::crash {

/// @brief Default number of trailing log lines captured into a report.
inline constexpr std::size_t kDefaultTailLogLines{50};

/// @brief Default maximum number of finalized reports kept on disk.
inline constexpr std::size_t kDefaultMaxReports{20};

/**
* @brief A finalized crash report.
*
* Aggregates the raw signal snapshot (timestamp/pid/signal/raw stack
* addresses) with the tail of the logger file, ready for JSON storage.
*
* @note Plain struct; serialize via toJson().
* @warning None
* @since 0.19.0
* @ingroup crash
*/
struct CrashReport {
/// @brief Wall-clock time of the crash, in seconds since the epoch.
std::int64_t timestamp{0};

/// @brief Process id of the crashed shell.
std::int64_t pid{0};

/// @brief Signal number (e.g. SIGSEGV=11). 0 when no signal applies.
int signal{0};

/// @brief Human-readable signal name ("SIGSEGV", "SIGABRT", ...).
std::string signal_name;

/// @brief Bare stack frame addresses as hex strings; resolved in Phase 2.
std::vector<std::string> raw_frames;

/// @brief Tail of the logger file captured at finalize time.
std::vector<std::string> last_logs;

/**
* @brief Serializes the report to a pretty multi-line JSON string.
*
* @return JSON text (UTF-8); array elements are one per line.
* @throws None
* @note Strings are JSON-escaped.
* @warning None
* @since 0.19.0
* @ingroup crash
*/
std::string toJson() const;
};

/**
* @brief Maps a POSIX signal number to its canonical name.
*
* @param[in] signo Signal number.
* @return "SIGSEGV" etc., or "UNKNOWN" if unrecognized.
* @throws None
* @note None
* @warning None
* @since 0.19.0
* @ingroup crash
*/
std::string signalName(int signo);

} // namespace cf::crash
Loading
Loading