上一版只给了"模块+RVA"的栈,我据此先后推断成 AecDbCurveBase::subGetTransformedCopy 和 AecDbEntityCommon::base_DeepClone,两者都是错的 (用户在那里下断点不命中;新加的 PerfTraceMark 打点也没有触发)。本次用符号解析定死。 本次改动: - 新增 inc/PerfTraceMark.h:inline 的"某函数被调用过"打点,Debug 且 AEC_WRITEBACK_TRACE=1 才生效。AecRxBase.arx 不链接 CoreLibrary(静态库只被 CoreCommand 链接),拿不到 SectionPerf, 所以做成 header-only。 - 在 5 个候选入口打点:AecDbCurveBase::subGetTransformedCopy / subDeepClone、 AecDbEntityBase::subDeepClone、AecDbEntityCommon::base_DeepClone、CKTCadUtility::CloneObject。 跑一次开图,5 个都没触发 —— 这些候选被排除。 - 新增 tools/resolve_rva.ps1:用 dbghelp 的 SymLoadModuleEx / SymFromAddr 配本地 PDB, 把"模块+RVA"解析成函数名。Windows 没有自带 symbolizer,这个脚本很轻,可反复用。 解析结果(CoreCommand.arx): 0x58f051 AecDbEwPipeBase::copyFrom+0x41 0x7e1300 AecDbZLineStair::DrawGuarBar3d+0x2c0 0x7e09d0 AecDbZLineStair::DrawSteep3d+0xbe0 0x7ddcef AecDbZLineStair::Draw3d+0x34f 真实成因:Z 型楼梯画护栏(DrawGuarBar3d)时 copyFrom 一份管道实体, 而 AecDbEwPipeBase::copyFrom 用通用的 CopyObject(inc/AecDbEntityCommon.h:331) 做"整实体 dwgOutFields/dwgInFields 往返"来拷字段,于是截面被写成临时 SAT 文件再读回来。 而且这次序列化是多余的:copyFrom 随后把所有字段又用内存级方式拷了一遍 (曲线 clone/copyFrom、截面 clone/copyFrom、XData CopyFrom、m_arUserInfs append、 标志位/图层/线型/Breaks 逐项赋值),WriteUserInterface 写的也只是 m_arUserInfs。 所以最小修改是把那句 CopyObject 去掉,并补上源为 NULL 时清空目标的分支 (目前这个"清空"是 CopyObject 顺带完成的)。 本次只做定位与记录(分析写进 pipe-section-region-performance-plan.md 并纠正了上一版的误判), 不改 copyFrom 的行为——它影响所有管系实体的复制粘贴/块内拷贝,需要单独验证。
59 lines
1.6 KiB
C++
59 lines
1.6 KiB
C++
// PerfTraceMark.h —— 极轻量的"某函数被调用过"打点,用于在没有调试器时定位调用路径
|
||
//
|
||
// 用法:在候选函数入口写 PerfTraceMark("函数名"),
|
||
// 每次进程内该 tag 只记录第一次,追加到 %TEMP%\aec_writeback_trace.txt。
|
||
// 只在 Debug(NDEBUG 未定义)且环境变量 AEC_WRITEBACK_TRACE=1 时生效,发布版本零开销。
|
||
//
|
||
// 为什么不用 SectionPerf:AecRxBase.arx 不链接 CoreLibrary(静态库只被 CoreCommand 链接),
|
||
// 所以 AecRxBase 的文件里拿不到 SectionPerf 的符号。本头文件是 inline 的,哪个模块都能用。
|
||
|
||
#ifndef PERF_TRACE_MARK_H
|
||
#define PERF_TRACE_MARK_H
|
||
|
||
#include <stdio.h>
|
||
#include <stdlib.h>
|
||
#include <string.h>
|
||
#include <set>
|
||
#include <string>
|
||
|
||
inline void PerfTraceMark(const char* pszTag)
|
||
{
|
||
#ifndef NDEBUG
|
||
static bool s_bInit = false;
|
||
static bool s_bOn = false;
|
||
static std::set<std::string> s_seen;
|
||
|
||
if (!s_bInit)
|
||
{
|
||
s_bInit = true;
|
||
const char* psz = ::getenv("AEC_WRITEBACK_TRACE");
|
||
s_bOn = (psz != NULL && psz[0] != '0');
|
||
}
|
||
if (!s_bOn || pszTag == NULL)
|
||
return;
|
||
|
||
const std::string sTag(pszTag);
|
||
if (s_seen.find(sTag) != s_seen.end())
|
||
return;
|
||
s_seen.insert(sTag);
|
||
|
||
char szTmp[MAX_PATH + 1];
|
||
::memset(szTmp, 0, sizeof(szTmp));
|
||
if (::GetTempPathA(MAX_PATH, szTmp) == 0)
|
||
return;
|
||
std::string sPath(szTmp);
|
||
sPath += "aec_writeback_trace.txt";
|
||
|
||
FILE* fp = NULL;
|
||
if (fopen_s(&fp, sPath.c_str(), "ab") == 0 && fp != NULL)
|
||
{
|
||
fprintf(fp, "[mark] %s\n", pszTag);
|
||
fclose(fp);
|
||
}
|
||
#else
|
||
(void)pszTag;
|
||
#endif
|
||
}
|
||
|
||
#endif // PERF_TRACE_MARK_H
|