diff --git a/Printsetting.cpp b/Printsetting.cpp index 5e59092..00de811 100644 --- a/Printsetting.cpp +++ b/Printsetting.cpp @@ -250,6 +250,9 @@ void PrintSetting::updateSysStatusToUI(){ void PrintSetting::on_pushButtonApplyPulseFix_clicked() { + if(!sysPara) + return; + pulseNumFix->bindSysPara(sysPara); pulseNumFix->hide(); pulseNumFix->show(); @@ -596,22 +599,25 @@ void PrintSetting::setEnableSynTriggerSetting(bool flag){ void PrintSetting::applyPrintSettingsToSysPara(bool showTip) { + if(!sysPara) + return; + updateModelFromUI(); PrintSettingModel::applyToSysPara(m_model, sysPara); - if (sysPara) { + if (sysPara->devOp) { sysPara->devOp->setHighVoltageAmplitudetoSTM32(); sysPara->devOp->writeFpgaCalandar(0); sysPara->devOp->writeFlightCompCtrl(ui->enbFlightCompCtrl->isChecked(), ui->enbAccelDecelPredict->isChecked(), quint32(ui->flightCompWindowCycles->value())); - } - if(sysPara->devOp->writeFpgaPrintPara(true) != -1 && showTip) { - QMessageBox box(QMessageBox::Icon::Information, tr("提示"), tr("应用成功"), QMessageBox::Ok, nullptr); - auto btn = box.button(QMessageBox::Ok); - // btn->setFixedSize(80, 40); + if(sysPara->devOp->writeFpgaPrintPara(true) != -1 && showTip) { + QMessageBox box(QMessageBox::Icon::Information, tr("提示"), tr("应用成功"), QMessageBox::Ok, nullptr); + auto btn = box.button(QMessageBox::Ok); + // btn->setFixedSize(80, 40); - box.exec(); + box.exec(); + } } if(sysPara) @@ -802,6 +808,9 @@ void PrintSettingModel::applyToSysPara(const PrintSettingModel &model, SysPara * PrintSettingModel PrintSettingModel::fromSysPara(SysPara *sysPara) { PrintSettingModel model; + if(!sysPara) + return model; + model.leftRightInverse = sysPara->para.leftRightInverse; model.upDownInverse = sysPara->para.upDownInverse; model.mode0211 = sysPara->para.mode0211; diff --git a/QtUI.pro b/QtUI.pro index 0403994..3392725 100644 --- a/QtUI.pro +++ b/QtUI.pro @@ -32,6 +32,11 @@ greaterThan(QT_VER_MAJ, 5) | greaterThan(QT_VER_MIN, 9) QT += serialbus greaterThan(QT_MAJOR_VERSION, 4):QMAKE_CXXFLAGS += -fpermissive greaterThan(QT_MAJOR_VERSION, 4):CONFIG += c++11 +CONFIG += qt warn_on debug_and_release +QMAKE_CXXFLAGS += -g +#QMAKE_POST_LINK += ulimit -c unlimited +LIBS += -ldl + include(qjson/upQJson.pri) TEMPLATE = app diff --git a/doc/BarcodeEditor_Robustness_Analysis.md b/doc/BarcodeEditor_Robustness_Analysis.md new file mode 100644 index 0000000..50fc099 --- /dev/null +++ b/doc/BarcodeEditor_Robustness_Analysis.md @@ -0,0 +1,94 @@ +# BarcodeEditor 健壮性分析报告 + +## 涉及文件 + +| 文件 | 说明 | +|------|------| +| [scene/BarcodeEditor.h](../scene/BarcodeEditor.h) | `BarcodeViewModel` 结构体 + `BarcodeEditor` 类声明 | +| [scene/BarcodeEditor.cpp](../scene/BarcodeEditor.cpp) | `BarcodeEditor` 类实现 | + +--- + +## 🔴 高风险(必定崩溃) + +### 1. `applyCounterConfigToSysPara()` — `m_sysPara->devOp` 无 null 检查 +**文件**: [BarcodeEditor.cpp:260](../scene/BarcodeEditor.cpp#L260) +```cpp +m_sysPara->devOp->writeFpgaCalandar(0); +``` +**问题**:函数开头已经检查 `if (!m_sysPara) return;`,但 `m_sysPara` 不为 null 不代表 `m_sysPara->devOp` 不为 null。`devOp` 仍可能为 null,此时直接访问会崩溃。 + +--- + +## 🟡 中风险(特定条件下逻辑错误) + +### 2. `BarcodeViewModel::saveToXml()` — `QVariant::value()` 类型不一致 +**文件**: [BarcodeEditor.cpp:515-534](../scene/BarcodeEditor.cpp#L515-L534) +```cpp +if(vm.type == BARCODE_TYPE_BARCODE) { + DlgBarcodeFormatVM barcodeFormat = vm.format.value(); + // ... +} +else { + DlgQrcodeFormatVM qrcodeFormat = vm.format.value(); + // ... +} +``` +**问题**:如果 `vm.type` 与 `vm.format` 中实际存储的类型不一致,`QVariant::value()` 会返回默认构造对象,不会崩溃但会导致数据丢失。QVariant 本身保证不崩溃。 + +--- + +### 3. `bindViewModel()` 和 `onContentChanged()` — `m_sysPara` 可能为 null 传给 `makeCounterVM` +**文件**: [BarcodeEditor.cpp:135-140](../scene/BarcodeEditor.cpp#L135-L140) 和 [BarcodeEditor.cpp:467-472](../scene/BarcodeEditor.cpp#L467-L472) +```cpp +m_counterEditor->setViewModel(makeCounterVM(m_sysPara, ...)); +``` +**问题**:`m_sysPara` 可能为 null(`bindSysPara()` 未被调用时),虽然 `makeCounterVM` 内部已有 `if(!sysPara) return vm;` 保护,不会崩溃,但返回的空 VM 可能导致计数器显示不正确。 + +--- + +## 🟢 低风险(健壮性瑕疵,不会崩溃) + +### 4. 构造函数中 `m_dateFormatEditor` / `m_dateReplaceEditor` / `m_counterEditor` 未设置 parent +**文件**: [BarcodeEditor.cpp:50-55](../scene/BarcodeEditor.cpp#L50-L55) +```cpp +m_dateFormatEditor = new DateFormatEditor(); +m_dateReplaceEditor = new DateReplaceEditor(); +m_counterEditor = new CounterEditor(); +``` +析构中手动 delete,不会内存泄漏。但无 parent 意味着 Qt 父子对象机制不适用,需确保析构顺序正确。 + +--- + +## 已经做得好的地方 + +- ✅ `bindSysPara()` 对 null 输入有检查 + 日志输出 +- ✅ `applyCounterConfigToSysPara()` 对 `m_sysPara` null 有检查 + 日志输出 +- ✅ `m_dateFormatEditor` / `m_dateReplaceEditor` / `m_counterEditor` 在构造中 new,析构中 delete,内存管理正确 +- ✅ 删除标签页使用 `indexOf()` 检查 `-1` 后才移除,不会 remove 不存在的 tab +- ✅ `makeCounterVM()` 对 `sysPara` null 有检查,返回默认构造的 VM +- ✅ `eventFilter` 中对 `cursor < 0` 做了检查 +- ✅ `m_barcodeFormatEditor` / `m_qrcodeFormatEditor` 作为成员变量(非指针),构造时自动初始化,永远有效 +- ✅ `Q_DECLARE_METATYPE` 已声明,QVariant 类型转换安全 + +--- + +## 📋 需要修复的问题汇总 + +| 优先级 | 位置 | 问题 | 修复方案 | +|--------|------|------|----------| +| **P1** | `BarcodeEditor::applyCounterConfigToSysPara()` L260 | `m_sysPara->devOp` 无 null 检查 | 在访问 `devOp` 前加 `if(m_sysPara->devOp)` 保护 | +| **P2** | `bindViewModel()` L134-141 + `onContentChanged()` L466-473 | `m_sysPara` 可能为 null 传给 `makeCounterVM` | 虽然 `makeCounterVM` 自己能处理,但建议加日志或确认 `bindSysPara` 调用时机 | + +--- + +## 总结 + +BarcodeEditor 类的整体健壮性较好,主要问题仅有一个: + +**`applyCounterConfigToSysPara()` 中 `m_sysPara->devOp` 无 null 检查**,与先前 PrintSetting 报告中发现的同类问题相同,属于 `SysPara` 中 `devOp` 指针使用的普遍模式缺陷。 + +--- + +## 生成时间 +2026-06-29 \ No newline at end of file diff --git a/main.cpp b/main.cpp index db67329..1cc748b 100644 --- a/main.cpp +++ b/main.cpp @@ -1,3 +1,8 @@ +#define _GNU_SOURCE // 必须放在所有头文件之前 +#include // dl_iterate_phdr 的声明 +#include // dladdr 等 +#include // backtrace + #include #include #include @@ -20,6 +25,103 @@ #include "mainwidget.h" #include "InkJetPrinter.h" #include "Log/filelogger.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +QString gAppNameStr = "";//这是个全局变量,用于后续获取当前运行程序的绝对路径 + +// 全局变量保存基址 +static uintptr_t g_base_address = 0; + +// 回调函数:找到当前可执行文件的基址 +static int find_base_address(struct dl_phdr_info *info, size_t size, void *data) { + // 检查是否是主程序(第一个加载的通常是主程序) + if (info->dlpi_name == nullptr || info->dlpi_name[0] == '\0') { + // 获取第一个段的加载地址(通常是基址) + g_base_address = info->dlpi_addr; + return 1; // 停止遍历 + } + return 0; // 继续遍历 +} + +// 获取程序基址的函数 +uintptr_t get_base_address() { + if (g_base_address == 0) { + dl_iterate_phdr(find_base_address, nullptr); + } + return g_base_address; +} + +void createStackTraceFile(const char *fileName, const char *appName) +{ + std::ofstream file(fileName); + if (!file.is_open()) { + std::cerr << "Error opening file: " << fileName << std::endl; + return; + } + + void *array[100]; + size_t size; + char **strings; + + size = backtrace(array, 100); + if (size == 0) { + std::cerr << "No stack frames obtained" << std::endl; + return; + } + + // 获取程序基址(PIE需要) + uintptr_t base_addr = get_base_address(); + + strings = backtrace_symbols(array, size); + file << "Stack trace info:\n"; + file << "Base address: 0x" << std::hex << base_addr << std::dec << "\n\n"; + + for (size_t i = 0; i < size; ++i) { + file << strings[i] << std::endl; + + // 计算偏移地址:实际地址 - 基址 + uintptr_t addr = reinterpret_cast(array[i]); + uintptr_t offset = addr - base_addr; + + char cmd[512]; + // 使用偏移地址而不是实际地址 + snprintf(cmd, sizeof(cmd), "addr2line -e %s 0x%lx", appName, offset); + + FILE *fp = popen(cmd, "r"); + if (fp) { + char line[256]; + while (fgets(line, sizeof(line), fp)) { + file << " " << line; // 缩进显示更清晰 + } + pclose(fp); + } else { + std::cerr << "Failed to run addr2line command" << std::endl; + } + file << std::endl; + } + + free(strings); + file.close(); +} + +void signalHandler(int signum) +{ + // 直接指定固定的路径 + QString dumpFileName = "/home/plg/doc/dumpfile.txt"; + QString appName = gAppNameStr; + createStackTraceFile(dumpFileName.toUtf8().data(), appName.toUtf8().data()); + exit(signum); +} + //#define SYS_CN /* @@ -72,6 +174,12 @@ int main(int argc, char *argv[]) QApplication a(argc, argv); + gAppNameStr = QString::fromUtf8(argv[0]); + signal(SIGSEGV, signalHandler); + signal(SIGABRT, signalHandler); + signal(SIGILL, signalHandler); + signal(SIGFPE, signalHandler); + // 配置日志 QString configFileName = ":/conf/config.ini"; QSettings* logSettings = new QSettings(configFileName, QSettings::IniFormat, &a); diff --git a/scene/BarcodeEditor.cpp b/scene/BarcodeEditor.cpp index 8ce6723..3781e83 100644 --- a/scene/BarcodeEditor.cpp +++ b/scene/BarcodeEditor.cpp @@ -281,7 +281,8 @@ void BarcodeEditor::applyCounterConfigToSysPara() m_sysPara->para.eleEyeClearCntInSynMode = (m_sysPara->para.eleEyeClearCntInSynMode & 0xdf); // CNT2 = current } - m_sysPara->devOp->writeFpgaCalandar(0); + if(m_sysPara->devOp) + m_sysPara->devOp->writeFpgaCalandar(0); } void BarcodeEditor::onBtnFormatClicked() @@ -457,9 +458,11 @@ void BarcodeEditor::onContentChanged(QString content) else { if(ui->tabWidget->indexOf(m_counterEditor) == -1) { ui->tabWidget->addTab(m_counterEditor, tr(u8"计数器")); - m_counterEditor->setViewModel(makeCounterVM(m_sysPara, - static_cast(m_vm.counterViewModel.counterType), - m_vm.counterViewModel.digitalNum)); + if(m_sysPara) { + m_counterEditor->setViewModel(makeCounterVM(m_sysPara, + static_cast(m_vm.counterViewModel.counterType), + m_vm.counterViewModel.digitalNum)); + } } m_counterEditor->setFixedSize(ui->tabBarcode->size()); } @@ -594,4 +597,4 @@ void BarcodeEditor::on_cbBoxType_currentIndexChanged(int index) if (index == 0) return; m_qrcodeFormatEditor.setCodeType(ui->cbBoxType->currentText()); onQrcodeFormatConfirmed(); -} +} \ No newline at end of file diff --git a/scene/picscene.cpp b/scene/picscene.cpp index 43ce110..7449916 100644 --- a/scene/picscene.cpp +++ b/scene/picscene.cpp @@ -136,6 +136,10 @@ void PicScene::onFocusItemChanged(QGraphicsItem *newFocusItem, QGraphicsItem *ol return; } + connect(currFocusItem, &QObject::destroyed, this, [this](){ + currFocusItem = nullptr; + }); + QPoint pos(currFocusItem->x(), currFocusItem->y()); emit focusedItemPosChanged(pos); diff --git a/setnumdlg.cpp b/setnumdlg.cpp index 6410555..80a70f4 100644 --- a/setnumdlg.cpp +++ b/setnumdlg.cpp @@ -1,5 +1,10 @@ #include "setnumdlg.h" #include "ui_setnumdlg.h" +#include "SystemManage.h" +#include +#include "LogInfo.h" + +SystemManage* sysMgr = nullptr; SetNumDlg::SetNumDlg(QWidget *parent) : QDialog(parent), diff --git a/testprintwidget.cpp b/testprintwidget.cpp index df10558..2c91b7a 100644 --- a/testprintwidget.cpp +++ b/testprintwidget.cpp @@ -432,7 +432,8 @@ void TestPrintWidget::saveFile(){ } writer.writeEmptyElement("PrintSettings"); - PrintSettingModel::saveToXml(m_printSetting->model(), writer); + if(m_printSetting) + PrintSettingModel::saveToXml(m_printSetting->model(), writer); writer.writeEndElement(); // 结束根元素 writer.writeEndDocument(); // 结束文档 @@ -618,8 +619,10 @@ void TestPrintWidget::parseVbfile(QXmlStreamReader &reader, bool loadPrintSettin QXmlStreamAttributes attrs = reader.attributes(); PrintSettingModel model; PrintSettingModel::loadFromXml(model, attrs); - m_printSetting->setModel(model); - m_printSetting->applyPrintSettingsToSysPara(false); + if(m_printSetting) { + m_printSetting->setModel(model); + m_printSetting->applyPrintSettingsToSysPara(false); + } reader.skipCurrentElement(); } @@ -1119,7 +1122,8 @@ void TestPrintWidget::on_newfileButton_pressed() if (sysPara) { PrintSettingModel defModel = PrintSettingModel::defPrintSettingModel(); PrintSettingModel::applyToSysPara(defModel, sysPara); - m_printSetting->setModel(defModel); + if(m_printSetting) + m_printSetting->setModel(defModel); } sendButtonClickedSignal(21); //新建文件完成