上一版只给了"模块+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 的行为——它影响所有管系实体的复制粘贴/块内拷贝,需要单独验证。
97 lines
3.7 KiB
PowerShell
97 lines
3.7 KiB
PowerShell
# resolve_rva.ps1 - resolve module+RVA to function names using dbghelp + local PDB
|
|
# usage: powershell -NoProfile -ExecutionPolicy Bypass -File resolve_rva.ps1 `
|
|
# -Module "C:\...\CoreCommand.arx" -Rva 0x58f051,0x7ddcef,0x7e09d0,0x7e1300
|
|
|
|
param(
|
|
[Parameter(Mandatory=$true)][string]$Module,
|
|
[Parameter(Mandatory=$true)][string[]]$Rva,
|
|
[string]$SymbolPath = ''
|
|
)
|
|
|
|
$ErrorActionPreference = 'Stop'
|
|
$mod = (Resolve-Path $Module).Path
|
|
$dir = Split-Path $mod -Parent
|
|
if (-not $SymbolPath) { $SymbolPath = $dir }
|
|
|
|
$cs = @'
|
|
using System;
|
|
using System.Runtime.InteropServices;
|
|
|
|
public static class Dbg {
|
|
[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Ansi)]
|
|
public struct SYMBOL_INFO {
|
|
public uint SizeOfStruct;
|
|
public uint TypeIndex;
|
|
public ulong Reserved1;
|
|
public ulong Reserved2;
|
|
public uint Index;
|
|
public uint Size;
|
|
public ulong ModBase;
|
|
public uint Flags;
|
|
public ulong Value;
|
|
public ulong Address;
|
|
public uint Register;
|
|
public uint Scope;
|
|
public uint Tag;
|
|
public uint NameLen;
|
|
public uint MaxNameLen;
|
|
[MarshalAs(UnmanagedType.ByValArray, SizeConst=1024)]
|
|
public byte[] Name;
|
|
}
|
|
|
|
[DllImport("dbghelp.dll", SetLastError=true)]
|
|
public static extern bool SymInitialize(IntPtr hProcess, string UserSearchPath, bool fInvadeProcess);
|
|
[DllImport("dbghelp.dll", SetLastError=true)]
|
|
public static extern ulong SymLoadModuleEx(IntPtr hProcess, IntPtr hFile, string ImageName, string ModuleName,
|
|
ulong BaseOfDll, uint DllSize, IntPtr Data, uint Flags);
|
|
[DllImport("dbghelp.dll", SetLastError=true)]
|
|
public static extern bool SymFromAddr(IntPtr hProcess, ulong Address, out ulong Displacement, ref SYMBOL_INFO Symbol);
|
|
[DllImport("dbghelp.dll", SetLastError=true)]
|
|
public static extern bool SymCleanup(IntPtr hProcess);
|
|
[DllImport("dbghelp.dll")]
|
|
public static extern uint SymSetOptions(uint SymOptions);
|
|
|
|
public const uint SYMOPT_UNDNAME = 0x00000002;
|
|
public const uint SYMOPT_LOAD_LINES = 0x00000010;
|
|
public const uint SYMOPT_DEBUG = 0x80000000;
|
|
}
|
|
'@
|
|
Add-Type -TypeDefinition $cs -Language CSharp | Out-Null
|
|
|
|
[Dbg]::SymSetOptions([Dbg]::SYMOPT_UNDNAME) | Out-Null
|
|
$proc = [System.Diagnostics.Process]::GetCurrentProcess()
|
|
$h = $proc.Handle
|
|
if (-not [Dbg]::SymInitialize($h, $SymbolPath, $false)) {
|
|
Write-Output ("SymInitialize failed: " + [ComponentModel.Win32Exception]::new([Runtime.InteropServices.Marshal]::GetLastWin32Error()).Message)
|
|
exit 1
|
|
}
|
|
|
|
$base = [Dbg]::SymLoadModuleEx($h, [IntPtr]::Zero, $mod, $null, 0, 0, [IntPtr]::Zero, 0)
|
|
if ($base -eq 0) {
|
|
Write-Output ("SymLoadModuleEx failed: " + [ComponentModel.Win32Exception]::new([Runtime.InteropServices.Marshal]::GetLastWin32Error()).Message)
|
|
[Dbg]::SymCleanup($h) | Out-Null
|
|
exit 1
|
|
}
|
|
Write-Output ("module : " + $mod)
|
|
Write-Output ("base : 0x{0:x}" -f $base)
|
|
Write-Output ("sympath : " + $SymbolPath)
|
|
Write-Output ""
|
|
|
|
foreach ($r in (($Rva -join ',').Split(',') | Where-Object { $_ -ne '' })) {
|
|
$off = [Convert]::ToUInt64($r, 16)
|
|
$addr = $base + $off
|
|
$sym = New-Object Dbg+SYMBOL_INFO
|
|
$sym.SizeOfStruct = 88
|
|
$sym.MaxNameLen = 1024
|
|
$sym.Name = New-Object byte[] 1024
|
|
[uint64]$disp = 0
|
|
if ([Dbg]::SymFromAddr($h, $addr, [ref]$disp, [ref]$sym)) {
|
|
$name = [Text.Encoding]::ASCII.GetString($sym.Name, 0, [int]$sym.NameLen)
|
|
Write-Output ("0x{0:x} {1}+0x{2:x} {3}" -f $off, $name, $disp, "")
|
|
} else {
|
|
Write-Output ("0x{0:x} <no symbol> (err {1})" -f $off, [Runtime.InteropServices.Marshal]::GetLastWin32Error())
|
|
}
|
|
}
|
|
|
|
[Dbg]::SymCleanup($h) | Out-Null
|