已合并
fix:修复dump文件未完整落盘 #3266
yring_8创建于 6月30日
fix:修复dump文件未完整落盘 #3266
已合并
yring_8创建于 6月30日
已删除 :9.1.0合入到cann/runtime9.1.0
15 个文件变更+356-134
@@ -38,7 +38,10 @@ public:
38 bool Push(T &value)38 bool Push(T &value)
39 {39 {
40 std::unique_lock<std::mutex> lk(mtx_);40 std::unique_lock<std::mutex> lk(mtx_);
41- cvPop_.wait(lk, [this] { return !this->IsFull() || this->quit_;});41+ cvPop_.wait(lk, [this] { return !this->IsFullUnlocked() || this->quit_;});
42+ if (this->quit_) {
43+ return false;
44+ }
42 dataQueue_.push(value);45 dataQueue_.push(value);
43 cvPush_.notify_all();46 cvPush_.notify_all();
44 return true;47 return true;
@@ -47,8 +50,8 @@ public:
47 bool Pop(T &value)50 bool Pop(T &value)
48 {51 {
49 std::unique_lock<std::mutex> lk(mtx_);52 std::unique_lock<std::mutex> lk(mtx_);
50- cvPush_.wait(lk, [this] { return !this->IsEmpty() || this->quit_; });53+ cvPush_.wait(lk, [this] { return !this->IsEmptyUnlocked() || this->quit_; });
51- if (!this->IsEmpty()) {54+ if (!this->IsEmptyUnlocked()) {
52 value = this->dataQueue_.front();55 value = this->dataQueue_.front();
53 this->dataQueue_.pop();56 this->dataQueue_.pop();
54 cvPop_.notify_all();57 cvPop_.notify_all();
@@ -60,10 +63,49 @@ public:
60 63 
61 bool IsEmpty() const64 bool IsEmpty() const
62 {65 {
63- return dataQueue_.empty();66+ std::lock_guard<std::mutex> lk(mtx_);
67+ return IsEmptyUnlocked();
64 }68 }
65 69 
66 bool IsFull() const70 bool IsFull() const
71+ {
72+ std::lock_guard<std::mutex> lk(mtx_);
73+ return IsFullUnlocked();
74+ }
75+ 
76+ void Init()
77+ {
78+ std::lock_guard<std::mutex> lk(mtx_);
79+ quit_ = false;
80+ std::queue<T>().swap(dataQueue_);
81+ }
82+ 
83+ void Quit()
84+ {
85+ std::lock_guard<std::mutex> lk(mtx_);
86+ quit_ = true;
87+ cvPush_.notify_all();
88+ cvPop_.notify_all();
89+ }
90+ 
91+ uint32_t Size() const
92+ {
93+ std::lock_guard<std::mutex> lk(mtx_);
94+ return dataQueue_.size();
95+ }
96+ 
97+ void SetPath(std::string path)
98+ {
99+ path_ = path;
100+ }
101+ 
102+private:
103+ bool IsEmptyUnlocked() const
104+ {
105+ return dataQueue_.empty();
106+ }
107+ 
108+ bool IsFullUnlocked() const
67 {109 {
68 struct sysinfo info;110 struct sysinfo info;
69 const size_t queueSize = 60;111 const size_t queueSize = 60;
@@ -84,33 +126,6 @@ public:
84 return (info.freeram < (info.totalram * (1 - ADX_QUEUE_FULL_SIZE))) && dataQueue_.size() > queueSize;126 return (info.freeram < (info.totalram * (1 - ADX_QUEUE_FULL_SIZE))) && dataQueue_.size() > queueSize;
85 }127 }
86 128 
87- void Init()
88- {
89- std::lock_guard<std::mutex> lk(mtx_);
90- quit_ = false;
91- }
92- 
93- void Quit()
94- {
95- std::lock_guard<std::mutex> lk(mtx_);
96- if (!quit_) {
97- quit_ = true;
98- cvPush_.notify_all();
99- cvPop_.notify_all();
100- }
101- }
102- 
103- uint32_t Size() const
104- {
105- return dataQueue_.size();
106- }
107- 
108- void SetPath(std::string path)
109- {
110- path_ = path;
111- }
112- 
113-private:
114 uint64_t InitMemLimit() const {129 uint64_t InitMemLimit() const {
115 std::ifstream memLimitV1(MEM_LIMIT_V1);130 std::ifstream memLimitV1(MEM_LIMIT_V1);
116 std::ifstream memLimitV2(MEM_LIMIT_V2);131 std::ifstream memLimitV2(MEM_LIMIT_V2);
@@ -12,6 +12,7 @@
12#include <map>12#include <map>
13#include <cinttypes>13#include <cinttypes>
14#include <functional>14#include <functional>
15+#include <pthread.h>
15#include "mmpa_api.h"16#include "mmpa_api.h"
16#include "adx_log.h"17#include "adx_log.h"
17#include "file_utils.h"18#include "file_utils.h"
@@ -21,7 +22,6 @@
21#include "adx_dump_process.h"22#include "adx_dump_process.h"
22#include "ide_os_type.h"23#include "ide_os_type.h"
23namespace Adx {24namespace Adx {
24-static const int32_t WAIT_RECORD_FILE_FINISH_TIME = 500;
25static const std::size_t MAX_IP_LENGTH = 16;25static const std::size_t MAX_IP_LENGTH = 16;
26#if !defined(ADUMP_SOC_HOST) || ADUMP_SOC_HOST == 126#if !defined(ADUMP_SOC_HOST) || ADUMP_SOC_HOST == 1
27constexpr char STRING_BIN[] = ".bin";27constexpr char STRING_BIN[] = ".bin";
@@ -139,6 +139,11 @@ AdxDumpRecord::AdxDumpRecord()
139 : dumpRecordFlag_(true),139 : dumpRecordFlag_(true),
140 dumpInitNum_(0)140 dumpInitNum_(0)
141{141{
142+ int32_t ret = pthread_atfork(
143+ AdxDumpRecord::PrepareFork, AdxDumpRecord::PostForkParent, AdxDumpRecord::PostForkChild);
144+ if (ret != 0) {
145+ IDE_LOGW("call pthread_atfork failed, ret: %d", ret);
146+ }
142#if !defined(ADUMP_SOC_HOST) || ADUMP_SOC_HOST == 1147#if !defined(ADUMP_SOC_HOST) || ADUMP_SOC_HOST == 1
143 fileNameStatus_.reserve(FILENAME_CHECK_SIZE_MAX);148 fileNameStatus_.reserve(FILENAME_CHECK_SIZE_MAX);
144 for (size_t idx = 0; idx < FILENAME_CHECK_SIZE_MAX; ++idx) {149 for (size_t idx = 0; idx < FILENAME_CHECK_SIZE_MAX; ++idx) {
@@ -186,13 +191,6 @@ bool AdxDumpRecord::CanShutdownServer() const
186 return dumpInitNum_ <= 1;191 return dumpInitNum_ <= 1;
187}192}
188 193 
189-/**
190- * @brief initialize record file
191- * @param [in] recordPath : record file Path
192- * @return
193- * IDE_DAEMON_ERROR : falied
194- * IDE_DAEMON_OK : success
195- */
196int32_t AdxDumpRecord::Init(const std::string &hostPid)194int32_t AdxDumpRecord::Init(const std::string &hostPid)
197{195{
198 // non-soc case196 // non-soc case
@@ -234,13 +232,38 @@ int32_t AdxDumpRecord::Init(const std::string &hostPid)
234 if (!dumpPath_.empty() && dumpPath_.back() != OS_SPLIT_CHAR) {232 if (!dumpPath_.empty() && dumpPath_.back() != OS_SPLIT_CHAR) {
235 dumpPath_ += OS_SPLIT_CHAR;233 dumpPath_ += OS_SPLIT_CHAR;
236 }234 }
237- hostDumpDataInfoQueue_.Init();235+ 
236+ std::lock_guard<std::mutex> lock(recordMutex_);
237+ if (hostDumpDataInfoQueue_ == nullptr) {
238+ hostDumpDataInfoQueue_.reset(new(std::nothrow) BoundQueueMemory<HostDumpDataInfo>());
239+ IDE_CTRL_VALUE_FAILED(hostDumpDataInfoQueue_ != nullptr, return IDE_DAEMON_ERROR,
240+ "Failed to new hostDumpDataInfoQueue");
241+ }
242+ hostDumpDataInfoQueue_->Init();
238 IDE_LOGI("record remote dump temp path: %s", dumpPath_.c_str());243 IDE_LOGI("record remote dump temp path: %s", dumpPath_.c_str());
239- hostDumpDataInfoQueue_.SetPath(dumpPath_);244+ hostDumpDataInfoQueue_->SetPath(dumpPath_);
240 dumpRecordFlag_ = true;245 dumpRecordFlag_ = true;
241 return IDE_DAEMON_OK;246 return IDE_DAEMON_OK;
242}247}
243 248 
249+int32_t AdxDumpRecord::StartRecord()
250+{
251+ std::lock_guard<std::mutex> lock(recordMutex_);
252+ if (recordThread_.joinable()) {
253+ IDE_LOGI("dump record thread has been started, no need to start again");
254+ return IDE_DAEMON_OK;
255+ }
256+ dumpRecordFlag_ = true;
257+ try {
258+ recordThread_ = std::thread(&AdxDumpRecord::RecordDumpInfo, this);
259+ } catch (const std::exception &ex) {
260+ dumpRecordFlag_ = false;
261+ IDE_LOGE("Create the dump record thread failed, message: %s", ex.what());
262+ return IDE_DAEMON_ERROR;
263+ }
264+ return IDE_DAEMON_OK;
265+}
266+ 
244/**267/**
245 * @brief initialize record file268 * @brief initialize record file
246 * @param [in] recordPath : record file Path269 * @param [in] recordPath : record file Path
@@ -250,20 +273,41 @@ int32_t AdxDumpRecord::Init(const std::string &hostPid)
250 */273 */
251int32_t AdxDumpRecord::UnInit()274int32_t AdxDumpRecord::UnInit()
252{275{
276+ std::lock_guard<std::mutex> lock(recordMutex_);
253 IDE_LOGI("start to dump uninit");277 IDE_LOGI("start to dump uninit");
254 dumpRecordFlag_ = false;278 dumpRecordFlag_ = false;
255-#if !defined(__IDE_UT) && !defined(__IDE_ST)279+ if (hostDumpDataInfoQueue_ != nullptr) {
256- while (!DumpDataQueueIsEmpty()) {280+ hostDumpDataInfoQueue_->Quit();
257- mmSleep(WAIT_RECORD_FILE_FINISH_TIME);281+ }
282+ if (recordThread_.joinable()) {
283+ recordThread_.join();
258 }284 }
259-#if !defined(ANDROID)
260- this->hostDumpDataInfoQueue_.Quit();
261-#endif
262-#endif
263 IDE_LOGI("dump uninit success");285 IDE_LOGI("dump uninit success");
264 return IDE_DAEMON_OK;286 return IDE_DAEMON_OK;
265}287}
266 288 
289+void AdxDumpRecord::PrepareFork()
290+{
291+ Instance().recordMutex_.lock();
292+}
293+ 
294+void AdxDumpRecord::PostForkParent()
295+{
296+ Instance().recordMutex_.unlock();
297+}
298+ 
299+void AdxDumpRecord::PostForkChild()
300+{
301+ auto &instance = Instance();
302+ instance.dumpRecordFlag_ = false;
303+ if (instance.recordThread_.joinable()) {
304+ instance.recordThread_.detach();
305+ }
306+ 
307+ (void)instance.hostDumpDataInfoQueue_.release();
308+ instance.recordMutex_.unlock();
309+}
310+ 
267/**311/**
268 * @brief record dump data to disk312 * @brief record dump data to disk
269 * @param [in] dumpChunk : dump chunk313 * @param [in] dumpChunk : dump chunk
@@ -654,9 +698,9 @@ void AdxDumpRecord::RecordDumpInfo()
654{698{
655 IDE_RUN_LOGI("start dump thread, remote dump record temp path : %s.", dumpPath_.c_str());699 IDE_RUN_LOGI("start dump thread, remote dump record temp path : %s.", dumpPath_.c_str());
656 uint32_t chunkHeaderLen = static_cast<uint32_t>(sizeof(DumpChunk));700 uint32_t chunkHeaderLen = static_cast<uint32_t>(sizeof(DumpChunk));
657- while (dumpRecordFlag_ || !DumpDataQueueIsEmpty()) {701+ while (hostDumpDataInfoQueue_ != nullptr && (dumpRecordFlag_ || !DumpDataQueueIsEmpty())) {
658 HostDumpDataInfo data = {nullptr, 0};702 HostDumpDataInfo data = {nullptr, 0};
659- if (!hostDumpDataInfoQueue_.Pop(data)) {703+ if (!hostDumpDataInfoQueue_->Pop(data)) {
660 continue;704 continue;
661 }705 }
662 706 
@@ -674,13 +718,14 @@ void AdxDumpRecord::RecordDumpInfo()
674 "bufLen(%u) exceeds actual data buffer size(%u bytes), fileName: %s",718 "bufLen(%u) exceeds actual data buffer size(%u bytes), fileName: %s",
675 dumpChunk->bufLen, data.recvLen - chunkHeaderLen, dumpChunk->fileName);719 dumpChunk->bufLen, data.recvLen - chunkHeaderLen, dumpChunk->fileName);
676 720 
677- IDE_LOGI("Queue pop data success! filename: %s, offset: %" PRId64 ", bufLen: %u bytes, isLast: %u, flag: %d.",721+ IDE_LOGI("Queue pop data success! filename: %s, offset: %" PRId64 ", bufLen: %u bytes, "
678- dumpChunk->fileName, dumpChunk->offset, dumpChunk->bufLen, dumpChunk->isLastChunk, dumpChunk->flag);722+ "isLast: %u, flag: %d, remaining queue size: %u.",
723+ dumpChunk->fileName, dumpChunk->offset, dumpChunk->bufLen, dumpChunk->isLastChunk,
724+ dumpChunk->flag, hostDumpDataInfoQueue_->Size());
679#if !defined(ADUMP_SOC_HOST) || ADUMP_SOC_HOST == 1725#if !defined(ADUMP_SOC_HOST) || ADUMP_SOC_HOST == 1
atomgit-bot
atomgit-botatomgit-bot6月30日

🟡 Medium Priority

RecordDumpInfo() (第 725 行) 和 RecordDumpDataToQueue() (第 775 行) 中新增的日志调用 hostDumpDataInfoQueue_->Size() 读取 std::queue::size(),但未持有队列内部的 mtx_ 互斥锁。

BoundQueueMemory::Size() 的实现直接返回 dataQueue_.size(),不加锁: uint32_t Size() const { return dataQueue_.size(); }

Push() / Pop() 在持有 mtx_ 的情况下并发修改 dataQueue_。在 Pop() 返回(释放锁)之后立即调用 Size(),或在 Push() 返回后立即调用 Size(),与另一个线程的 Push/Pop 操作形成对 dataQueue_ 容器的无锁并发读写——这是 C++ 标准定义的数据竞争(UB)。实际影响有限(日志值可能略微不准),但从正确性角度属于未定义行为。

likedislike
不准确?
680 if (FileNameCheck(*dumpChunk)) {726 if (FileNameCheck(*dumpChunk)) {
681 IDE_CTRL_VALUE_FAILED_NODO(StatsDataParsing(*dumpChunk), continue,727 IDE_CTRL_VALUE_FAILED_NODO(StatsDataParsing(*dumpChunk), continue,
682 "Failed to parse dump data with file name %s", dumpChunk->fileName);728 "Failed to parse dump data with file name %s", dumpChunk->fileName);
683- IDE_LOGD("New popped data process success");
684 continue;729 continue;
685 }730 }
686#endif731#endif
@@ -701,7 +746,6 @@ void AdxDumpRecord::RecordDumpInfo()
701 } else if (!RecordDumpDataToDisk(*dumpChunk)) {746 } else if (!RecordDumpDataToDisk(*dumpChunk)) {
702 IDE_LOGE("failed to record dump data to disk.");747 IDE_LOGE("failed to record dump data to disk.");
703 }748 }
704- IDE_LOGD("new popped data process success");
705 }749 }
706 IDE_LOGI("exit record file thread");750 IDE_LOGI("exit record file thread");
707}751}
@@ -715,15 +759,22 @@ void AdxDumpRecord::RecordDumpInfo()
715 */759 */
716bool AdxDumpRecord::RecordDumpDataToQueue(HostDumpDataInfo &info)760bool AdxDumpRecord::RecordDumpDataToQueue(HostDumpDataInfo &info)
717{761{
718- if (hostDumpDataInfoQueue_.IsFull()) {762+ if (hostDumpDataInfoQueue_ == nullptr) {
763+ IDE_LOGW("dump data queue is not initialized, drop this data packet.");
764+ return false;
765+ }
766+ if (hostDumpDataInfoQueue_->IsFull()) {
719 const std::string tipFull = "Memory usage exceeds 85%, the dump data queue is full";767 const std::string tipFull = "Memory usage exceeds 85%, the dump data queue is full";
720 const std::string tipReduce = "Please reduce model batches, images or dump layers";768 const std::string tipReduce = "Please reduce model batches, images or dump layers";
721 const std::string tipMemory = "Or clear the used memory or increase the maximum memory";769 const std::string tipMemory = "Or clear the used memory or increase the maximum memory";
722 IDE_LOGW("%s. %s. %s.", tipFull.c_str(), tipReduce.c_str(), tipMemory.c_str());770 IDE_LOGW("%s. %s. %s.", tipFull.c_str(), tipReduce.c_str(), tipMemory.c_str());
723 return false;771 return false;
724 } else {772 } else {
725- hostDumpDataInfoQueue_.Push(info);773+ if (!hostDumpDataInfoQueue_->Push(info)) {
726- IDE_LOGD("Insert dump data to queue success.");774+ IDE_LOGW("dump data queue has quit, drop this data packet.");
775+ return false;
776+ }
777+ IDE_LOGI("Insert dump data to queue success, queue size: %u.", hostDumpDataInfoQueue_->Size());
727 }778 }
728 779 
729 return true;780 return true;
@@ -738,7 +789,7 @@ bool AdxDumpRecord::RecordDumpDataToQueue(HostDumpDataInfo &info)
738 */789 */
739bool AdxDumpRecord::DumpDataQueueIsEmpty() const790bool AdxDumpRecord::DumpDataQueueIsEmpty() const
740{791{
741- return hostDumpDataInfoQueue_.IsEmpty();792+ return hostDumpDataInfoQueue_ == nullptr || hostDumpDataInfoQueue_->IsEmpty();
742}793}
743 794 
744void AdxDumpRecord::SetDumpPath(const std::string &dumpPath)795void AdxDumpRecord::SetDumpPath(const std::string &dumpPath)
@@ -9,10 +9,13 @@
9 */9 */
10#ifndef ADX_DUMP_RECORE_H10#ifndef ADX_DUMP_RECORE_H
11#define ADX_DUMP_RECORE_H11#define ADX_DUMP_RECORE_H
12+#include <atomic>
13+#include <memory>
12#include <mutex>14#include <mutex>
13#include <string>15#include <string>
14#include <cstdint>16#include <cstdint>
15#include <sstream>17#include <sstream>
18+#include <thread>
16#if !defined(ADUMP_SOC_HOST) || ADUMP_SOC_HOST == 119#if !defined(ADUMP_SOC_HOST) || ADUMP_SOC_HOST == 1
17#include "proto/dump_task.pb.h"20#include "proto/dump_task.pb.h"
18#endif21#endif
@@ -84,6 +87,7 @@ public:
84 int32_t Init(const std::string &hostPid);87 int32_t Init(const std::string &hostPid);
85 int32_t UnInit();88 int32_t UnInit();
86 void SetWorkPath(const std::string &path);89 void SetWorkPath(const std::string &path);
90+ int32_t StartRecord();
87 void RecordDumpInfo();91 void RecordDumpInfo();
88 bool RecordDumpDataToQueue(HostDumpDataInfo &info);92 bool RecordDumpDataToQueue(HostDumpDataInfo &info);
89 bool DumpDataQueueIsEmpty() const;93 bool DumpDataQueueIsEmpty() const;
@@ -97,6 +101,9 @@ public:
97 101 
98private:102private:
99 bool JudgeRemoteFalg(const std::string &msg) const;103 bool JudgeRemoteFalg(const std::string &msg) const;
104+ static void PrepareFork();
105+ static void PostForkParent();
106+ static void PostForkChild();
100#if !defined(ADUMP_SOC_HOST) || ADUMP_SOC_HOST == 1107#if !defined(ADUMP_SOC_HOST) || ADUMP_SOC_HOST == 1
101 bool DumpDataToCallback(const std::string &filename, const std::string &dumpData, int64_t offSet, int32_t flag);108 bool DumpDataToCallback(const std::string &filename, const std::string &dumpData, int64_t offSet, int32_t flag);
102 bool StatsDataParsing(const DumpChunk &dumpChunk);109 bool StatsDataParsing(const DumpChunk &dumpChunk);
@@ -117,11 +124,14 @@ private:
117#endif124#endif
118 125 
119private:126private:
120- bool dumpRecordFlag_;127+ std::atomic<bool> dumpRecordFlag_;
121 std::string dumpPath_;128 std::string dumpPath_;
122 std::string workPath_;129 std::string workPath_;
123- BoundQueueMemory<HostDumpDataInfo> hostDumpDataInfoQueue_;130+ std::unique_ptr<BoundQueueMemory<HostDumpDataInfo>> hostDumpDataInfoQueue_;
124 int32_t dumpInitNum_;131 int32_t dumpInitNum_;
132+ std::thread recordThread_;
133+ std::mutex recordMutex_;
134+ 
125#if !defined(ADUMP_SOC_HOST) || ADUMP_SOC_HOST == 1135#if !defined(ADUMP_SOC_HOST) || ADUMP_SOC_HOST == 1
126 uint64_t dumpStatsItem_{0};136 uint64_t dumpStatsItem_{0};
127 uint32_t filenameIndex_{0};137 uint32_t filenameIndex_{0};
@@ -22,13 +22,29 @@ constexpr int64_t MAX_BUFFER_LENGTH = 512;
22static const std::string MAPPING_FILE_NAME = "mapping.csv";22static const std::string MAPPING_FILE_NAME = "mapping.csv";
23} // namespace23} // namespace
24 24 
25-File::File(const std::string &path, int32_t flag, mmMode_t mode) : filePath_(path), fd_(INVALID_FILE_FD)25+File::File(const std::string &path, int32_t flag, mmMode_t mode, bool lazyOpen)
26+ : filePath_(path), fd_(INVALID_FILE_FD), flag_(flag), mode_(mode)
26{27{
28+ if (lazyOpen) {
29+ return;
30+ }
27 if (Open(flag, mode) != ADUMP_SUCCESS) {31 if (Open(flag, mode) != ADUMP_SUCCESS) {
28 fd_ = INVALID_FILE_FD;32 fd_ = INVALID_FILE_FD;
29 }33 }
30}34}
31 35 
36+int32_t File::EnsureOpen()
37+{
38+ if (fd_ != INVALID_FILE_FD) {
39+ return ADUMP_SUCCESS;
40+ }
41+ if (Open(flag_, mode_) != ADUMP_SUCCESS) {
42+ fd_ = INVALID_FILE_FD;
43+ return ADUMP_FAILED;
44+ }
45+ return ADUMP_SUCCESS;
46+}
47+ 
32File::~File()48File::~File()
33{49{
34 (void)Close();50 (void)Close();
@@ -16,9 +16,10 @@
16namespace Adx {16namespace Adx {
17class File {17class File {
18public:18public:
19- explicit File(const std::string &path, int32_t flag, mmMode_t mode = M_IRUSR | M_IWUSR);19+ explicit File(const std::string &path, int32_t flag, mmMode_t mode = M_IRUSR | M_IWUSR, bool lazyOpen = false);
20 ~File();20 ~File();
21 int32_t IsFileOpen() const;21 int32_t IsFileOpen() const;
22+ int32_t EnsureOpen();
22 int64_t Write(const char * const buffer, int64_t length) const;23 int64_t Write(const char * const buffer, int64_t length) const;
23 int64_t Read(char *buffer, int64_t length) const;24 int64_t Read(char *buffer, int64_t length) const;
24 static int32_t Copy(const std::string &srcPath, const std::string &dstPath);25 static int32_t Copy(const std::string &srcPath, const std::string &dstPath);
@@ -29,6 +30,8 @@ private:
29 int32_t AddMapping(const std::string &filePath, const std::string &fileName, const std::string &hashName);30 int32_t AddMapping(const std::string &filePath, const std::string &fileName, const std::string &hashName);
30 std::string filePath_;31 std::string filePath_;
31 int32_t fd_;32 int32_t fd_;
33+ int32_t flag_;
34+ mmMode_t mode_;
32};35};
33} // namespace Adx36} // namespace Adx
34#endif // ADUMP_COMMON_FILE_H37#endif // ADUMP_COMMON_FILE_H
@@ -15,43 +15,17 @@
15#include "log/adx_log.h"15#include "log/adx_log.h"
16#include "adx_dump_record.h"16#include "adx_dump_record.h"
17namespace Adx {17namespace Adx {
18-static IdeThreadArg AdxDumpRecordSocProcess(const IdeThreadArg arg)
19-{
20- UNUSED(arg);
21- AdxDumpRecord::Instance().RecordDumpInfo();
22- return nullptr;
23-}
24- 
25-static IdeThreadArg AdxDataDumpServerSocProcess(const IdeThreadArg arg)
26-{
27- UNUSED(arg);
28- mmUserBlock_t funcBlock;
29- funcBlock.pulArg = nullptr;
30- mmThread tid = 0;
31- funcBlock.procFunc = AdxDumpRecordSocProcess;
32- int ret = Thread::CreateTaskWithDefaultAttr(tid, funcBlock);
33- if (ret != EN_OK) {
34- return nullptr;
35- }
36- 
37- (void)mmJoinTask(&tid);
38- return nullptr;
39-}
40- 
41int32_t AdxSocDataDumpInit(const std::string &hostPid)18int32_t AdxSocDataDumpInit(const std::string &hostPid)
42{19{
43- mmUserBlock_t funcBlock;
44- funcBlock.procFunc = AdxDataDumpServerSocProcess;
45- funcBlock.pulArg = nullptr;
46- mmThread tid = 0;
47 // soc case, pass host pid to record instance20 // soc case, pass host pid to record instance
48 int ret = AdxDumpRecord::Instance().Init(hostPid);21 int ret = AdxDumpRecord::Instance().Init(hostPid);
49 if (ret != IDE_DAEMON_OK) {22 if (ret != IDE_DAEMON_OK) {
50 IDE_LOGE("AdxDumpRecord init failed.");23 IDE_LOGE("AdxDumpRecord init failed.");
51 return IDE_DAEMON_ERROR;24 return IDE_DAEMON_ERROR;
52 }25 }
53- ret = Thread::CreateDetachTaskWithDefaultAttr(tid, funcBlock);26+ ret = AdxDumpRecord::Instance().StartRecord();
54- if (ret != EN_OK) {27+ if (ret != IDE_DAEMON_OK) {
28+ IDE_LOGE("start dump record thread failed.");
55 return IDE_DAEMON_ERROR;29 return IDE_DAEMON_ERROR;
56 }30 }
57 IDE_LOGI("Adx soc dump thread has been started.");31 IDE_LOGI("Adx soc dump thread has been started.");
@@ -218,9 +218,27 @@ void DumpFile::SetTensorBuffer(const std::vector<TensorBuffer> &tensorBuffer)
218 }218 }
219}219}
220 220 
221+bool DumpFile::HasDumpData() const
222+{
223+ if (!inputs_.empty() || !outputs_.empty() || !workspaces_.empty() || !inputBuffer_.empty()) {
224+ return true;
225+ }
226+#if !defined(ADUMP_SOC_HOST) || ADUMP_SOC_HOST == 1
227+ if (!mc2Spaces_.empty()) {
228+ return true;
229+ }
230+#endif
231+ return false;
232+}
233+ 
221int32_t DumpFile::Dump(std::vector<std::string> &record)234int32_t DumpFile::Dump(std::vector<std::string> &record)
222{235{
223- int32_t ret = file_.IsFileOpen();236+ if (!HasDumpData()) {
237+ IDE_LOGI("No dump data, skip dump file.");
238+ return ADUMP_SUCCESS;
239+ }
240+ 
241+ int32_t ret = file_.EnsureOpen();
224 if (ret != ADUMP_SUCCESS) {242 if (ret != ADUMP_SUCCESS) {
225 return ret;243 return ret;
226 }244 }
@@ -24,7 +24,7 @@ namespace Adx {
24class DumpFile {24class DumpFile {
25public:25public:
26 DumpFile(const uint32_t deviceId, const std::string &filePath) : deviceId_(deviceId),26 DumpFile(const uint32_t deviceId, const std::string &filePath) : deviceId_(deviceId),
27- file_(filePath, M_RDWR | M_CREAT | M_APPEND, M_IRUSR | M_IWUSR) {}27+ file_(filePath, M_RDWR | M_CREAT | M_APPEND, M_IRUSR | M_IWUSR, true) {}
28 void SetHeader(const std::string &opName);28 void SetHeader(const std::string &opName);
29 void SetInputTensors(const std::vector<DumpTensor> &inputTensors);29 void SetInputTensors(const std::vector<DumpTensor> &inputTensors);
30 void SetOutputTensors(const std::vector<DumpTensor> &outputTensors);30 void SetOutputTensors(const std::vector<DumpTensor> &outputTensors);
@@ -37,6 +37,7 @@ public:
37#endif37#endif
38 38 
39private:39private:
40+ bool HasDumpData() const;
40 void SetAicInfo(std::vector<std::string> &record);41 void SetAicInfo(std::vector<std::string> &record);
41 int32_t WriteHeader() const;42 int32_t WriteHeader() const;
42 int32_t WriteInputTensors();43 int32_t WriteInputTensors();
@@ -43,13 +43,6 @@ IdeThreadArg AdxDataDumpServerProcess(const IdeThreadArg arg)
43 return nullptr;43 return nullptr;
44}44}
45 45 
46-IdeThreadArg AdxDumpRecordProcess(const IdeThreadArg arg)
47-{
48- UNUSED(arg);
49- AdxDumpRecord::Instance().RecordDumpInfo();
50- return nullptr;
51-}
52- 
53static bool IsOnDeviceSide()46static bool IsOnDeviceSide()
54{47{
55 uint32_t platformInfo = static_cast<uint32_t>(SysPlatformType::INVALID);48 uint32_t platformInfo = static_cast<uint32_t>(SysPlatformType::INVALID);
@@ -77,10 +70,9 @@ int32_t AdxDataDumpServerInit()
77 IDE_LOGE("AdxDumpRecord init failed.");70 IDE_LOGE("AdxDumpRecord init failed.");
78 return IDE_DAEMON_ERROR;71 return IDE_DAEMON_ERROR;
79 }72 }
80- funcBlock.procFunc = AdxDumpRecordProcess;73+ ret = AdxDumpRecord::Instance().StartRecord();
81- funcBlock.pulArg = nullptr;74+ if (ret != IDE_DAEMON_OK) {
82- ret = Thread::CreateDetachTaskWithDefaultAttr(tid, funcBlock);75+ IDE_LOGE("start dump record thread failed.");
83- if (ret != EN_OK) {
84 return IDE_DAEMON_ERROR;76 return IDE_DAEMON_ERROR;
85 }77 }
86 78 
@@ -110,23 +102,25 @@ int32_t AdxDataDumpServerUnInit()
110 }102 }
111 103 
112 IDE_LOGI("start to do dump uninit");104 IDE_LOGI("start to do dump uninit");
113- if (AdxDumpRecord::Instance().UnInit() != IDE_DAEMON_OK) {
114- IDE_LOGE("dump record uninit failed");
115- return IDE_DAEMON_ERROR;
116- }
117- 
118 std::string hostPid;105 std::string hostPid;
119 ADX_GET_ENV(MM_ENV_ASCEND_HOSTPID, hostPid);106 ADX_GET_ENV(MM_ENV_ASCEND_HOSTPID, hostPid);
120- if (IsOnDeviceSide() && !hostPid.empty()) {107+ int32_t ret = IDE_DAEMON_OK;
121- AdxDumpRecord::Instance().UpdateDumpInitNum(false);108+ bool isHelper = IsOnDeviceSide() && !hostPid.empty();
122- IDE_LOGI("dump server not start on helper device");109+ if (!isHelper && g_manager.Exit() != IDE_DAEMON_OK) {
123- return IDE_DAEMON_OK;
124- }
125- if (g_manager.Exit() != IDE_DAEMON_OK) {
126 IDE_LOGE("AdxServerManager Exit failed");110 IDE_LOGE("AdxServerManager Exit failed");
127- return IDE_DAEMON_ERROR;111+ ret = IDE_DAEMON_ERROR;
112+ }
113+ if (isHelper) {
114+ IDE_LOGI("dump server not start on helper device");
128 }115 }
129 116 
130- AdxDumpRecord::Instance().UpdateDumpInitNum(false);117+ if (AdxDumpRecord::Instance().UnInit() != IDE_DAEMON_OK) {
131- return IDE_DAEMON_OK;118+ IDE_LOGE("dump record uninit failed");
119+ ret = IDE_DAEMON_ERROR;
120+ }
121+ 
122+ if (ret == IDE_DAEMON_OK) {
123+ AdxDumpRecord::Instance().UpdateDumpInitNum(false);
124+ }
125+ return ret;
132}126}
@@ -172,6 +172,7 @@ int32_t KernelDfxDumper::UnInitTask()
172void KernelDfxDumper::UnInit()172void KernelDfxDumper::UnInit()
173{173{
174 std::lock_guard<std::mutex> lock(mutex_);174 std::lock_guard<std::mutex> lock(mutex_);
175+ destructed_ = true;
175 UnInitTask();176 UnInitTask();
176 dumpPath_.clear();177 dumpPath_.clear();
177 enabledDfxTypes_.clear();178 enabledDfxTypes_.clear();
@@ -207,7 +208,6 @@ void KernelDfxDumper::PostForkChild()
207 instance.dumpDfxInfoQueue_ = nullptr;208 instance.dumpDfxInfoQueue_ = nullptr;
208 }209 }
209 instance.mutex_.unlock();210 instance.mutex_.unlock();
210- IDE_LOGI("KernelDfxDumper has been reset in the child process.");
211}211}
212 212 
213KernelDfxDumper::KernelDfxDumper()213KernelDfxDumper::KernelDfxDumper()
@@ -263,6 +263,7 @@ int32_t KernelDfxDumper::EnableDfxDumper(const DumpDfxConfig config)
263 return ADUMP_SUCCESS;263 return ADUMP_SUCCESS;
264 }264 }
265 std::lock_guard<std::mutex> lock(mutex_);265 std::lock_guard<std::mutex> lock(mutex_);
266+ destructed_ = false;
266 std::set<rtKernelDfxInfoType> rtDfxTypes;267 std::set<rtKernelDfxInfoType> rtDfxTypes;
267 GetRegisterDfxTypes(config.dfxTypes, rtDfxTypes);268 GetRegisterDfxTypes(config.dfxTypes, rtDfxTypes);
268 for (auto& rtDfxType : rtDfxTypes) {269 for (auto& rtDfxType : rtDfxTypes) {
@@ -326,6 +327,9 @@ std::string KernelDfxDumper::GetDfxInfoFilePath(uint32_t coreId, std::string &co
326int32_t KernelDfxDumper::DumpKernelDfxInfo(rtKernelDfxInfoType dfxType, uint32_t coreType, uint32_t coreId,327int32_t KernelDfxDumper::DumpKernelDfxInfo(rtKernelDfxInfoType dfxType, uint32_t coreType, uint32_t coreId,
327 const uint8_t *buffer, size_t length)328 const uint8_t *buffer, size_t length)
328{329{
330+ std::lock_guard<std::mutex> lock(mutex_);
331+ IDE_CTRL_VALUE_WARN(!destructed_, return ADUMP_FAILED, "KernelDfxDumper has been destructed.");
332+ 
329 std::string dfxTypeStr = GetDfxTypeStr(dfxType);333 std::string dfxTypeStr = GetDfxTypeStr(dfxType);
330 std::string coreTypeStr = GetCoreTypeStr(coreType);334 std::string coreTypeStr = GetCoreTypeStr(coreType);
331 IDE_CTRL_VALUE_WARN(IsEnabled(dfxType), return ADUMP_FAILED,335 IDE_CTRL_VALUE_WARN(IsEnabled(dfxType), return ADUMP_FAILED,
@@ -59,6 +59,7 @@ private:
59 std::string dumpPath_;59 std::string dumpPath_;
60 std::atomic<bool> taskInit_ = false;60 std::atomic<bool> taskInit_ = false;
61 std::atomic<bool> taskRunning_ = false;61 std::atomic<bool> taskRunning_ = false;
62+ bool destructed_ = false;
62 std::thread taskThread_;63 std::thread taskThread_;
63 std::mutex mutex_;64 std::mutex mutex_;
64 std::unique_ptr<BoundQueueMemory<DumpDfxInfo>> dumpDfxInfoQueue_;65 std::unique_ptr<BoundQueueMemory<DumpDfxInfo>> dumpDfxInfoQueue_;
@@ -492,15 +492,16 @@ TEST_F(DumpArgsCallbackUtest, Test_Constructor_WithDisplayName)
492 Tools::CaseWorkspace ws("Test_Constructor_WithDisplayName");492 Tools::CaseWorkspace ws("Test_Constructor_WithDisplayName");
493 rtExceptionInfo exception = {0};493 rtExceptionInfo exception = {0};
494 InitExceptionInfo(exception);494 InitExceptionInfo(exception);
495- 495+ 
496 ExceptionDumpInfo info = {0};496 ExceptionDumpInfo info = {0};
497 info.coreId = 0;497 info.coreId = 0;
498 info.coreType = 1;498 info.coreType = 1;
499 SetKernelName(info, "test");499 SetKernelName(info, "test");
500 SetDisplayName(info, "display");500 SetDisplayName(info, "display");
501 501 
502+ // 无 dfx args / extra tensor / 日志时,Dump 应跳过并返回成功,且不生成空文件
502 DumpArgsCallback callback(exception, info, ws.Root());503 DumpArgsCallback callback(exception, info, ws.Root());
503- EXPECT_EQ(callback.Dump(), ADUMP_FAILED);504+ EXPECT_EQ(callback.Dump(), ADUMP_SUCCESS);
504}505}
505 506 
506TEST_F(DumpArgsCallbackUtest, Test_Constructor_EmptyDisplayName)507TEST_F(DumpArgsCallbackUtest, Test_Constructor_EmptyDisplayName)
@@ -508,13 +509,14 @@ TEST_F(DumpArgsCallbackUtest, Test_Constructor_EmptyDisplayName)
508 Tools::CaseWorkspace ws("Test_Constructor_EmptyDisplayName");509 Tools::CaseWorkspace ws("Test_Constructor_EmptyDisplayName");
509 rtExceptionInfo exception = {0};510 rtExceptionInfo exception = {0};
510 InitExceptionInfo(exception);511 InitExceptionInfo(exception);
511- 512+ 
512 ExceptionDumpInfo info = {0};513 ExceptionDumpInfo info = {0};
513 SetKernelName(info, "test");514 SetKernelName(info, "test");
514 info.kernelDisplayName[0] = '\0';515 info.kernelDisplayName[0] = '\0';
515- 516+ 
517+ // 无数据无日志的空 dump 跳过,返回成功且不落盘
516 DumpArgsCallback callback(exception, info, ws.Root());518 DumpArgsCallback callback(exception, info, ws.Root());
517- EXPECT_EQ(callback.Dump(), ADUMP_FAILED);519+ EXPECT_EQ(callback.Dump(), ADUMP_SUCCESS);
518}520}
519 521 
520TEST_F(DumpArgsCallbackUtest, Test_DumpExtraTensors_SkipNull)522TEST_F(DumpArgsCallbackUtest, Test_DumpExtraTensors_SkipNull)
@@ -92,14 +92,10 @@ TEST_F(DumpFileUtest, Test_DumpData)
92 int32_t ret = dumpFile.Dump(logRecord);92 int32_t ret = dumpFile.Dump(logRecord);
93 EXPECT_EQ(ret, ADUMP_SUCCESS);93 EXPECT_EQ(ret, ADUMP_SUCCESS);
94 94 
95+ // 所有 tensorAddr 均为 nullptr,无任何真实 tensor/workspace/buffer 数据进入落盘容器,
96+ // 日志仅为附加信息;新口径下无真实数据不落盘,故文件不应生成
95 DumpFileChecker checker;97 DumpFileChecker checker;
96- EXPECT_EQ(checker.Load(dumpFilePath), true);98+ EXPECT_EQ(checker.Load(dumpFilePath), false);
97- EXPECT_EQ(checker.CheckHead("test_op"), true);
98- // tensorAddr is nullptr, so SetInputTensors/SetOutputTensors AND SetWorkspaces skip it;
99- // only the header is written
100- EXPECT_EQ(checker.CheckInputTensorNum(0), true);
101- EXPECT_EQ(checker.CheckOutputTensorNum(0), true);
102- EXPECT_EQ(checker.CheckWorkspaceNum(0), true);
103}99}
104 100 
105// TEST_F(DumpFileUtest, Test_Dump_With_CopyDeviceData_Fail)101// TEST_F(DumpFileUtest, Test_Dump_With_CopyDeviceData_Fail)
@@ -9,6 +9,8 @@
9 */9 */
10#include <gtest/gtest.h>10#include <gtest/gtest.h>
11#include <sstream>11#include <sstream>
12+#include <unistd.h>
13+#include <sys/wait.h>
12#include "mockcpp/mockcpp.hpp"14#include "mockcpp/mockcpp.hpp"
13#define protected public15#define protected public
14#define private public16#define private public
@@ -85,6 +87,109 @@ TEST_F(ADX_DUMP_RECORD_TEST, UnInit)
85 EXPECT_EQ(IDE_DAEMON_OK, ret);87 EXPECT_EQ(IDE_DAEMON_OK, ret);
86}88}
87 89 
90+TEST_F(ADX_DUMP_RECORD_TEST, StartRecordAndUnInit)
91+{
92+ // start the consumer thread, then UnInit drains the queue and joins it
93+ Adx::AdxDumpRecord::Instance().Init("");
94+ EXPECT_EQ(IDE_DAEMON_OK, Adx::AdxDumpRecord::Instance().StartRecord());
95+ // starting again while running is a no-op success
96+ EXPECT_EQ(IDE_DAEMON_OK, Adx::AdxDumpRecord::Instance().StartRecord());
97+ 
98+ const char *srcFile = "adx_data_dump_server_manager";
99+ uint32_t dataLen = strlen(srcFile) + 1 + sizeof(Adx::DumpChunk);
100+ MsgProto *msg = Adx::AdxMsgProto::CreateMsgPacket(IDE_DUMP_REQ, 0, nullptr, dataLen);
101+ Adx::SharedPtr<MsgProto> msgPtr(msg, IdeXfree);
102+ Adx::HostDumpDataInfo info = {msgPtr, dataLen};
103+ EXPECT_EQ(true, Adx::AdxDumpRecord::Instance().RecordDumpDataToQueue(info));
104+ 
105+ EXPECT_EQ(IDE_DAEMON_OK, Adx::AdxDumpRecord::Instance().UnInit());
106+ // after join, the queue must be fully drained
107+ EXPECT_EQ(true, Adx::AdxDumpRecord::Instance().DumpDataQueueIsEmpty());
108+}
109+ 
110+// queue is released after fork in child / before Init; access must be null-safe (no crash)
111+TEST_F(ADX_DUMP_RECORD_TEST, QueueNullSafeAfterRelease)
112+{
113+ Adx::AdxDumpRecord::Instance().Init("");
114+ EXPECT_NE(nullptr, Adx::AdxDumpRecord::Instance().hostDumpDataInfoQueue_.get());
115+ // simulate the post-fork-child release: drop ownership of the inherited queue
116+ (void)Adx::AdxDumpRecord::Instance().hostDumpDataInfoQueue_.release();
117+ EXPECT_EQ(nullptr, Adx::AdxDumpRecord::Instance().hostDumpDataInfoQueue_.get());
118+ 
119+ // null queue must be treated as empty so the consumer loop can exit
120+ EXPECT_EQ(true, Adx::AdxDumpRecord::Instance().DumpDataQueueIsEmpty());
121+ 
122+ // enqueue on a null queue must fail gracefully instead of crashing
123+ const char *srcFile = "adx_data_dump_server_manager";
124+ uint32_t dataLen = strlen(srcFile) + 1 + sizeof(Adx::DumpChunk);
125+ MsgProto *msg = Adx::AdxMsgProto::CreateMsgPacket(IDE_DUMP_REQ, 0, nullptr, dataLen);
126+ Adx::SharedPtr<MsgProto> msgPtr(msg, IdeXfree);
127+ Adx::HostDumpDataInfo info = {msgPtr, dataLen};
128+ EXPECT_EQ(false, Adx::AdxDumpRecord::Instance().RecordDumpDataToQueue(info));
129+ 
130+ // RecordDumpInfo on a null queue must return immediately, not spin
131+ Adx::AdxDumpRecord::Instance().dumpRecordFlag_ = true;
132+ Adx::AdxDumpRecord::Instance().RecordDumpInfo();
133+ 
134+ // a fresh Init rebuilds the queue so dump can work again
135+ EXPECT_EQ(IDE_DAEMON_OK, Adx::AdxDumpRecord::Instance().Init(""));
136+ EXPECT_NE(nullptr, Adx::AdxDumpRecord::Instance().hostDumpDataInfoQueue_.get());
137+}
138+ 
139+// directly drive the pthread_atfork callbacks: child must reset to a clean, restartable state
140+TEST_F(ADX_DUMP_RECORD_TEST, ForkCallbacksResetChildState)
141+{
142+ Adx::AdxDumpRecord::Instance().Init("");
143+ EXPECT_EQ(IDE_DAEMON_OK, Adx::AdxDumpRecord::Instance().StartRecord());
144+ 
145+ // PrepareFork locks recordMutex_; PostForkParent unlocks it (parent path keeps queue & thread)
146+ Adx::AdxDumpRecord::PrepareFork();
147+ Adx::AdxDumpRecord::PostForkParent();
148+ EXPECT_NE(nullptr, Adx::AdxDumpRecord::Instance().hostDumpDataInfoQueue_.get());
149+ 
150+ // parent still owns a joinable thread; reclaim it cleanly
151+ EXPECT_EQ(IDE_DAEMON_OK, Adx::AdxDumpRecord::Instance().UnInit());
152+ 
153+ // PrepareFork + PostForkChild: child detaches the ghost thread, releases the inherited queue,
154+ // clears the flag, and unlocks the mutex -> clean restartable state
155+ Adx::AdxDumpRecord::Instance().Init("");
156+ EXPECT_EQ(IDE_DAEMON_OK, Adx::AdxDumpRecord::Instance().StartRecord());
157+ Adx::AdxDumpRecord::PrepareFork();
158+ Adx::AdxDumpRecord::PostForkChild();
159+ EXPECT_EQ(false, Adx::AdxDumpRecord::Instance().dumpRecordFlag_);
160+ EXPECT_EQ(nullptr, Adx::AdxDumpRecord::Instance().hostDumpDataInfoQueue_.get());
161+ EXPECT_EQ(false, Adx::AdxDumpRecord::Instance().recordThread_.joinable());
162+ 
163+ // after the child reset, dump can be rebuilt and torn down again without hang
164+ EXPECT_EQ(IDE_DAEMON_OK, Adx::AdxDumpRecord::Instance().Init(""));
165+ EXPECT_EQ(IDE_DAEMON_OK, Adx::AdxDumpRecord::Instance().StartRecord());
166+ EXPECT_EQ(IDE_DAEMON_OK, Adx::AdxDumpRecord::Instance().UnInit());
167+}
168+ 
169+// real fork: child rebuilds its own dump pipeline, parent keeps running
170+TEST_F(ADX_DUMP_RECORD_TEST, ForkChildRebuildDump)
171+{
172+ Adx::AdxDumpRecord::Instance().Init("");
173+ EXPECT_EQ(IDE_DAEMON_OK, Adx::AdxDumpRecord::Instance().StartRecord());
174+ 
175+ pid_t pid = fork();
176+ if (pid == 0) {
177+ // child: pthread_atfork(PostForkChild) already reset state; rebuild a fresh pipeline
178+ int32_t initRet = Adx::AdxDumpRecord::Instance().Init("");
179+ int32_t startRet = Adx::AdxDumpRecord::Instance().StartRecord();
180+ int32_t uninitRet = Adx::AdxDumpRecord::Instance().UnInit();
181+ _exit((initRet == IDE_DAEMON_OK && startRet == IDE_DAEMON_OK &&
182+ uninitRet == IDE_DAEMON_OK) ? 0 : 1);
183+ } else if (pid > 0) {
184+ int status = 0;
185+ waitpid(pid, &status, 0);
186+ EXPECT_TRUE(WIFEXITED(status));
187+ EXPECT_EQ(0, WEXITSTATUS(status));
188+ // parent's own pipeline is intact and can be torn down cleanly
189+ EXPECT_EQ(IDE_DAEMON_OK, Adx::AdxDumpRecord::Instance().UnInit());
190+ }
191+}
192+ 
88TEST_F(ADX_DUMP_RECORD_TEST, UpdateDumpInitNum)193TEST_F(ADX_DUMP_RECORD_TEST, UpdateDumpInitNum)
89{194{
90 Adx::AdxDumpRecord::Instance().UpdateDumpInitNum(true);195 Adx::AdxDumpRecord::Instance().UpdateDumpInitNum(true);
@@ -154,6 +259,8 @@ TEST_F(ADX_DUMP_RECORD_TEST, RecordDumpDataToQueue)
154 MsgProto *msg = Adx::AdxMsgProto::CreateMsgPacket(IDE_DUMP_REQ, 0, nullptr, dataLen);259 MsgProto *msg = Adx::AdxMsgProto::CreateMsgPacket(IDE_DUMP_REQ, 0, nullptr, dataLen);
155 Adx::SharedPtr<MsgProto> msgPtr(msg, IdeXfree);260 Adx::SharedPtr<MsgProto> msgPtr(msg, IdeXfree);
156 Adx::HostDumpDataInfo info = {msgPtr, dataLen};261 Adx::HostDumpDataInfo info = {msgPtr, dataLen};
262+ Adx::AdxDumpRecord::Instance().Init(""); // 复位队列 quit_,使 Push 可入队
263+ Adx::AdxDumpRecord::Instance().dumpRecordFlag_ = false; // 让 RecordDumpInfo 排空后即退出,避免同步调用阻塞
157 bool ret = Adx::AdxDumpRecord::Instance().RecordDumpDataToQueue(info);264 bool ret = Adx::AdxDumpRecord::Instance().RecordDumpDataToQueue(info);
158 EXPECT_EQ(true, ret);265 EXPECT_EQ(true, ret);
159 Adx::AdxDumpRecord::Instance().RecordDumpInfo();266 Adx::AdxDumpRecord::Instance().RecordDumpInfo();
@@ -228,6 +335,33 @@ TEST_F(ADX_DUMP_RECORD_TEST, RecordDumpDataToFullQueueLimit)
228 EXPECT_EQ(false, ret);335 EXPECT_EQ(false, ret);
229}336}
230 337 
338+TEST_F(ADX_DUMP_RECORD_TEST, PushAfterQuitRejected)
339+{
340+ const char *srcFile = "adx_data_dump_server_manager";
341+ uint32_t dataLen = strlen(srcFile) + 1 + sizeof(Adx::DumpChunk);
342+ MsgProto *msg = Adx::AdxMsgProto::CreateMsgPacket(IDE_DUMP_REQ, 0, nullptr, dataLen);
343+ Adx::SharedPtr<MsgProto> msgPtr(msg, IdeXfree);
344+ Adx::HostDumpDataInfo info = {msgPtr, dataLen};
345+ 
346+ MOCKER(sysinfo).stubs().will(invoke(SysinfoAmpleMem));
347+ BoundQueueMemory<HostDumpDataInfo> mem;
348+ 
349+ // Quit 之前正常入队
350+ EXPECT_EQ(true, mem.Push(info));
351+ EXPECT_EQ(1u, mem.Size());
352+ 
353+ // Quit 之后拒绝入队,队列大小不再增长(避免 join 后数据滞留丢失)
354+ mem.Quit();
355+ EXPECT_EQ(false, mem.Push(info));
356+ EXPECT_EQ(1u, mem.Size());
357+ 
358+ // Init 复位:清空上一轮残留数据 + 恢复入队能力
359+ mem.Init();
360+ EXPECT_EQ(0u, mem.Size()); // 残留数据被清空
361+ EXPECT_EQ(true, mem.Push(info));
362+ EXPECT_EQ(1u, mem.Size());
363+}
364+ 
231TEST_F(ADX_DUMP_RECORD_TEST, RecordDumpInfoToMindspore)365TEST_F(ADX_DUMP_RECORD_TEST, RecordDumpInfoToMindspore)
232{366{
233 const char *srcFile = "adx_data_dump_server_manager";367 const char *srcFile = "adx_data_dump_server_manager";
@@ -236,6 +370,8 @@ TEST_F(ADX_DUMP_RECORD_TEST, RecordDumpInfoToMindspore)
236 MsgProto *msg = Adx::AdxMsgProto::CreateMsgPacket(IDE_DUMP_REQ, 0, nullptr, dataLen);370 MsgProto *msg = Adx::AdxMsgProto::CreateMsgPacket(IDE_DUMP_REQ, 0, nullptr, dataLen);
237 Adx::SharedPtr<MsgProto> msgPtr(msg, IdeXfree);371 Adx::SharedPtr<MsgProto> msgPtr(msg, IdeXfree);
238 Adx::HostDumpDataInfo info = {msgPtr, dataLen};372 Adx::HostDumpDataInfo info = {msgPtr, dataLen};
373+ Adx::AdxDumpRecord::Instance().Init(""); // 复位队列 quit_,使 Push 可入队
374+ Adx::AdxDumpRecord::Instance().dumpRecordFlag_ = false; // 让 RecordDumpInfo 排空后即退出,避免同步调用阻塞
239 bool ret = Adx::AdxDumpRecord::Instance().RecordDumpDataToQueue(info);375 bool ret = Adx::AdxDumpRecord::Instance().RecordDumpDataToQueue(info);
240 EXPECT_EQ(true, ret);376 EXPECT_EQ(true, ret);
241 Adx::AdxDumpRecord::Instance().RecordDumpInfo();377 Adx::AdxDumpRecord::Instance().RecordDumpInfo();
@@ -249,6 +385,8 @@ TEST_F(ADX_DUMP_RECORD_TEST, RecordOptimizedMode)
249 MsgProto *msg = Adx::AdxMsgProto::CreateMsgPacket(IDE_DUMP_REQ, 0, nullptr, dataLen);385 MsgProto *msg = Adx::AdxMsgProto::CreateMsgPacket(IDE_DUMP_REQ, 0, nullptr, dataLen);
250 Adx::SharedPtr<MsgProto> msgPtr(msg, IdeXfree);386 Adx::SharedPtr<MsgProto> msgPtr(msg, IdeXfree);
251 Adx::HostDumpDataInfo info = {msgPtr, dataLen};387 Adx::HostDumpDataInfo info = {msgPtr, dataLen};
388+ Adx::AdxDumpRecord::Instance().Init(""); // 复位队列 quit_,使 Push 可入队
389+ Adx::AdxDumpRecord::Instance().dumpRecordFlag_ = false; // 让 RecordDumpInfo 排空后即退出,避免同步调用阻塞
252 bool ret = Adx::AdxDumpRecord::Instance().RecordDumpDataToQueue(info);390 bool ret = Adx::AdxDumpRecord::Instance().RecordDumpDataToQueue(info);
253 EXPECT_EQ(true, ret);391 EXPECT_EQ(true, ret);
254 uint64_t statsItem = DUMP_STATS_MAX | DUMP_STATS_MIN | DUMP_STATS_AVG | DUMP_STATS_NAN | DUMP_STATS_NEG_INF | DUMP_STATS_POS_INF;392 uint64_t statsItem = DUMP_STATS_MAX | DUMP_STATS_MIN | DUMP_STATS_AVG | DUMP_STATS_NAN | DUMP_STATS_NEG_INF | DUMP_STATS_POS_INF;
@@ -86,7 +86,7 @@ TEST_F(ADX_DATADUMP_SERVER_UTEST, AdxDataDumpServerInit_AdxDumpRecord_Failed)
86TEST_F(ADX_DATADUMP_SERVER_UTEST, AdxDataDumpServerInit_CreateRecordProcess_Failed)86TEST_F(ADX_DATADUMP_SERVER_UTEST, AdxDataDumpServerInit_CreateRecordProcess_Failed)
87{87{
88 MOCKER(rtGetRunMode).stubs().will(returnValue(1));88 MOCKER(rtGetRunMode).stubs().will(returnValue(1));
89- MOCKER_CPP(&Thread::CreateDetachTaskWithDefaultAttr).stubs().will(returnValue(EN_ERROR));89+ MOCKER_CPP(&Adx::AdxDumpRecord::StartRecord).stubs().will(returnValue(IDE_DAEMON_ERROR));
90 EXPECT_EQ(IDE_DAEMON_ERROR, AdxDataDumpServerInit());90 EXPECT_EQ(IDE_DAEMON_ERROR, AdxDataDumpServerInit());
91 EXPECT_EQ(Adx::AdxDumpRecord::Instance().GetDumpInitNum(), 0);91 EXPECT_EQ(Adx::AdxDumpRecord::Instance().GetDumpInitNum(), 0);
92 EXPECT_EQ(IDE_DAEMON_OK, AdxDataDumpServerUnInit());92 EXPECT_EQ(IDE_DAEMON_OK, AdxDataDumpServerUnInit());
@@ -98,8 +98,7 @@ TEST_F(ADX_DATADUMP_SERVER_UTEST, AdxDataDumpServerInit_CreateServerProcess_Fail
98 MOCKER(rtGetRunMode).stubs().will(returnValue(1));98 MOCKER(rtGetRunMode).stubs().will(returnValue(1));
99 MOCKER_CPP(&Thread::CreateDetachTaskWithDefaultAttr)99 MOCKER_CPP(&Thread::CreateDetachTaskWithDefaultAttr)
100 .stubs()100 .stubs()
101- .will(returnValue(EN_OK))101+ .will(returnValue(EN_ERROR));
102- .then(returnValue(EN_ERROR));
103 EXPECT_EQ(IDE_DAEMON_ERROR, AdxDataDumpServerInit());102 EXPECT_EQ(IDE_DAEMON_ERROR, AdxDataDumpServerInit());
104 EXPECT_EQ(Adx::AdxDumpRecord::Instance().GetDumpInitNum(), 0);103 EXPECT_EQ(Adx::AdxDumpRecord::Instance().GetDumpInitNum(), 0);
105 EXPECT_EQ(IDE_DAEMON_OK, AdxDataDumpServerUnInit());104 EXPECT_EQ(IDE_DAEMON_OK, AdxDataDumpServerUnInit());