diag: 增加调用栈抓取,定位开图期 1140 次截面回写的成因

现象:开图期间 EwPipe::RegionAcisout 被调用 1140 次、耗时 2.38 s,
把几何又序列化回 SAT。之前只知现象不知来源。

新增 SectionPerf::TraceCallStack():用 RtlCaptureStackBackTrace + GetModuleHandleExW
把栈帧解析成 "模块名 + RVA" 并写进报告文件。正式代码里不开启,
只在 AEC_WRITEBACK_TRACE=1 时于 RegionAcisoutDwgOutFields 首次调用处抓一次。

结论(栈已定位):
  subViewportDraw -> base_ViewportDraw -> ... -> base_DeepClone
  -> AcDbObject::dwgOut -> AecDbEwPipeBase::dwgOutFields -> RegionAcisout

即:不是保存,而是绘制期的深拷贝。AecDbEntityCommon::base_DeepClone
(AecRxBase/AecDbEntityCommon.cpp:1265)用
  AcDbDeepCloneFiler filer; GetMe()->dwgOut(&filer); pClone->dwgIn(&filer);
实现拷贝,于是每次克隆实体,截面都要写一个临时 SAT 文件再读回来。
读的那一半现在已被 kText 缓存吃掉,剩下 acisOut 写文件这半(2.38 s)。

顺带修正一个文档缺陷:inc/SectionPerfCounter.h 里 %TEMP%\aec_section_perf.txt
的注释之前被转义吃掉变成了 %TEMP%<BEL>ec_section_perf.txt。

修法(跳过深拷贝期的区域序列化 / 直接写缓存里的 SAT 原文)都需要动
AecDbEntityCommon 的拷贝契约或先审计 m_pRegion 的写操作,风险面不一致,
只把分析与三种方案写进 pipe-section-region-performance-plan.md,本次不改行为。

验证:R230 Debug 编译通过并部署;AEC_WRITEBACK_TRACE=1 跑图一次,栈如文档所示。
This commit is contained in:
company-desktop committed 2026-10-28 20:49:42 +08:00
1 parent f8d10d5a3b
commit 67d6545e6c
4 files changed
+112 -3

No files matched your search

@@ -344,6 +344,52 @@ namespace SectionPerf
}
}
void TraceCallStack(const char* pszTag, int nSkip)
{
typedef USHORT(WINAPI* PFN_CaptureStack)(ULONG, ULONG, PVOID*, PULONG);
static PFN_CaptureStack s_pfn = NULL;
static bool s_bTried = false;
if (!s_bTried)
{
s_bTried = true;
HMODULE hNtdll = ::GetModuleHandleA("ntdll.dll");
if (hNtdll != NULL)
s_pfn = (PFN_CaptureStack)::GetProcAddress(hNtdll, "RtlCaptureStackBackTrace");
}
if (s_pfn == NULL)
return;
void* frames[24];
::memset(frames, 0, sizeof(frames));
const USHORT nGot = s_pfn((ULONG)(nSkip > 0 ? nSkip : 0), 24, frames, NULL);
std::string out = "\n[stack] ";
out += (pszTag != NULL ? pszTag : "?");
out += "\n";
char szLine[1024];
for (USHORT i = 0; i < nGot; ++i)
{
const DWORD64 nAddr = (DWORD64)frames[i];
HMODULE hMod = NULL;
if (!::GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
(LPCWSTR)nAddr, &hMod) || hMod == NULL)
continue;
WCHAR szPath[MAX_PATH];
::memset(szPath, 0, sizeof(szPath));
::GetModuleFileNameW(hMod, szPath, MAX_PATH);
const WCHAR* pszBase = wcsrchr(szPath, L'\\');
pszBase = (pszBase != NULL) ? pszBase + 1 : szPath;
char szMod[MAX_PATH * 2];
::memset(szMod, 0, sizeof(szMod));
::WideCharToMultiByte(CP_ACP, 0, pszBase, -1, szMod, sizeof(szMod) - 1, NULL, NULL);
sprintf_s(szLine, sizeof(szLine), " %-28s +0x%llx\n", szMod, (unsigned long long)(nAddr - (DWORD64)hMod));
out += szLine;
}
AppendReport(out.c_str());
}
void AppendReport(const char* pszText)
{
if (pszText == NULL)