debug_stack.cpp 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391
  1. /*
  2. ** Command & Conquer Generals Zero Hour(tm)
  3. ** Copyright 2025 Electronic Arts Inc.
  4. **
  5. ** This program is free software: you can redistribute it and/or modify
  6. ** it under the terms of the GNU General Public License as published by
  7. ** the Free Software Foundation, either version 3 of the License, or
  8. ** (at your option) any later version.
  9. **
  10. ** This program is distributed in the hope that it will be useful,
  11. ** but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. ** GNU General Public License for more details.
  14. **
  15. ** You should have received a copy of the GNU General Public License
  16. ** along with this program. If not, see <http://www.gnu.org/licenses/>.
  17. */
  18. /////////////////////////////////////////////////////////////////////////EA-V1
  19. // $File: //depot/GeneralsMD/Staging/code/Libraries/Source/debug/debug_stack.cpp $
  20. // $Author: KMorness $
  21. // $Revision: #2 $
  22. // $DateTime: 2005/01/19 15:02:33 $
  23. //
  24. // ©2003 Electronic Arts
  25. //
  26. // Stack walker
  27. //////////////////////////////////////////////////////////////////////////////
  28. #include "_pch.h"
  29. #include "dbghelp.h"
  30. // Definitions to allow run-time linking to the dbghelp.dll functions.
  31. #define DBGHELP(name,ret,par) typedef ret (WINAPI *name##Type) par;
  32. #include "debug_stack.inl"
  33. #undef DBGHELP
  34. #define DBGHELP(name,ret,par) name##Type _##name;
  35. static union
  36. {
  37. struct
  38. {
  39. #include "debug_stack.inl"
  40. };
  41. unsigned funcPtr[1];
  42. } gDbg;
  43. #undef DBGHELP
  44. #define DBGHELP(name,ret,par) #name,
  45. static char const *DebughelpFunctionNames[] =
  46. {
  47. #include "debug_stack.inl"
  48. NULL
  49. };
  50. #undef DBGHELP
  51. // local dbghelp.dll module handle
  52. static HMODULE g_dbghelp;
  53. // local flag that is true if we're using an old dbghelp.dll version
  54. static bool g_oldDbghelp;
  55. static void InitDbghelp(void)
  56. {
  57. // already called?
  58. if (g_dbghelp)
  59. return;
  60. // firstly check for dbghelp.dll in the EXE directory
  61. char dbgHelpPath[256];
  62. if (GetModuleFileName(NULL,dbgHelpPath,sizeof(dbgHelpPath)))
  63. {
  64. char *slash=strrchr(dbgHelpPath,'\\');
  65. if (slash)
  66. {
  67. strcpy(slash+1,"DBGHELP.DLL");
  68. g_dbghelp=::LoadLibrary(dbgHelpPath);
  69. }
  70. }
  71. if (!g_dbghelp)
  72. // load any version we can
  73. g_dbghelp=::LoadLibrary("DBGHELP.DLL");
  74. if (!g_dbghelp)
  75. return;
  76. // Get function addresses
  77. unsigned *funcptr=gDbg.funcPtr;
  78. for (unsigned k=0;DebughelpFunctionNames[k];++k,++funcptr)
  79. {
  80. *funcptr=(unsigned)GetProcAddress(g_dbghelp,DebughelpFunctionNames[k]);
  81. if (!*funcptr)
  82. break;
  83. }
  84. if (DebughelpFunctionNames[k])
  85. {
  86. // not all functions found -> clear them all
  87. while (funcptr!=gDbg.funcPtr)
  88. *--funcptr=NULL;
  89. }
  90. else
  91. {
  92. // Set options
  93. gDbg._SymSetOptions(gDbg._SymGetOptions()|SYMOPT_DEFERRED_LOADS|SYMOPT_LOAD_LINES);
  94. // Init module
  95. gDbg._SymInitialize((HANDLE)GetCurrentProcessId(),NULL,TRUE);
  96. // Check: are we using a newer version of dbghelp.dll?
  97. // (older versions have some serious issues.. err... bugs)
  98. if (!GetProcAddress(g_dbghelp,"SymEnumSymbolsForAddr"))
  99. g_oldDbghelp=true;
  100. }
  101. }
  102. //////////////////////////////////////////////////////////////////////////////
  103. DebugStackwalk::Signature::Signature(const Signature &src)
  104. {
  105. *this=src;
  106. }
  107. DebugStackwalk::Signature& DebugStackwalk::Signature::operator=(const Signature& src)
  108. {
  109. if (&src!=this)
  110. {
  111. m_numAddr=src.m_numAddr;
  112. memcpy(m_addr,src.m_addr,m_numAddr*sizeof(*m_addr));
  113. }
  114. return *this;
  115. }
  116. unsigned DebugStackwalk::Signature::GetAddress(int n) const
  117. {
  118. DFAIL_IF_MSG(n<0||n>=MAX_ADDR,n << "/" << MAX_ADDR) return 0;
  119. return m_addr[n];
  120. }
  121. void DebugStackwalk::Signature::GetSymbol(unsigned addr, char *buf, unsigned bufSize)
  122. {
  123. DFAIL_IF(!buf) return;
  124. DFAIL_IF(bufSize<64||bufSize>=0x80000000) return;
  125. InitDbghelp();
  126. char *bufEnd=buf+bufSize;
  127. *buf=0;
  128. buf+=wsprintf(buf,"%08x",addr);
  129. // determine module
  130. unsigned modBase=gDbg._SymGetModuleBase((HANDLE)GetCurrentProcessId(),addr);
  131. if (!modBase)
  132. {
  133. strcpy(buf," (unknown module)");
  134. return;
  135. }
  136. // illegal code ptr?
  137. if (IsBadReadPtr((void *)addr,4)||IsBadCodePtr((FARPROC)addr))
  138. {
  139. strcpy(buf," (invalid code addr)");
  140. return;
  141. }
  142. char symbolBuffer[512];
  143. GetModuleFileName((HMODULE)modBase,symbolBuffer,sizeof(symbolBuffer));
  144. char *p=strrchr(symbolBuffer,'\\'); // use filename only, strip off path
  145. p=p?p+1:symbolBuffer;
  146. *buf++=' ';
  147. strcpy(buf,p);
  148. buf+=strlen(buf);
  149. if (bufEnd-buf<32)
  150. return;
  151. buf+=wsprintf(buf,"+0x%x",addr-modBase);
  152. // determine symbol
  153. PIMAGEHLP_SYMBOL symPtr=(PIMAGEHLP_SYMBOL)symbolBuffer;
  154. memset(symPtr,0,sizeof(symbolBuffer));
  155. symPtr->SizeOfStruct=sizeof(IMAGEHLP_SYMBOL);
  156. symPtr->MaxNameLength=sizeof(symbolBuffer)-sizeof(IMAGEHLP_SYMBOL);
  157. DWORD displacement;
  158. if (!gDbg._SymGetSymFromAddr((HANDLE)GetCurrentProcessId(),addr,&displacement,symPtr))
  159. return;
  160. if ((unsigned int)(bufEnd-buf)<strlen(symPtr->Name)+16)
  161. return;
  162. buf+=wsprintf(buf,", %s+0x%x",symPtr->Name,displacement);
  163. // and line number
  164. IMAGEHLP_LINE line;
  165. memset(&line,0,sizeof(line));
  166. line.SizeOfStruct=sizeof(line);
  167. if (!gDbg._SymGetLineFromAddr((HANDLE)GetCurrentProcessId(),addr,&displacement,&line))
  168. return;
  169. p=strrchr(line.FileName,'\\'); // use filename only, strip off path
  170. p=p?p+1:line.FileName;
  171. if ((unsigned int)(bufEnd-buf)<strlen(p)+16)
  172. return;
  173. buf+=wsprintf(buf,", %s:%i+0x%x",p,line.LineNumber,displacement);
  174. }
  175. void DebugStackwalk::Signature::GetSymbol(unsigned addr,
  176. char *bufMod, unsigned sizeMod, unsigned *relMod,
  177. char *bufSym, unsigned sizeSym, unsigned *relSym,
  178. char *bufFile, unsigned sizeFile, unsigned *linePtr, unsigned *relLine)
  179. {
  180. InitDbghelp();
  181. if (bufMod) *bufMod=0;
  182. if (relMod) *relMod=0;
  183. if (bufSym) *bufSym=0;
  184. if (relSym) *relSym=0;
  185. if (bufFile) *bufFile=0;
  186. if (linePtr) *linePtr=0;
  187. if (relLine) *relLine=0;
  188. DFAIL_IF(bufMod&&sizeMod<16) return;
  189. DFAIL_IF(bufSym&&sizeSym<16) return;
  190. DFAIL_IF(bufFile&&sizeFile<16) return;
  191. // determine module
  192. unsigned modBase=gDbg._SymGetModuleBase((HANDLE)GetCurrentProcessId(),addr);
  193. if (!modBase)
  194. {
  195. if (bufMod)
  196. strcpy(bufMod,"(unknown mod)");
  197. if (bufSym)
  198. strcpy(bufSym,"(unknown)");
  199. return;
  200. }
  201. // illegal code ptr?
  202. if (IsBadReadPtr((void *)addr,4)||IsBadCodePtr((FARPROC)addr))
  203. {
  204. if (bufMod)
  205. strcpy(bufMod,"(inv code addr)");
  206. if (bufSym)
  207. strcpy(bufSym,"(unknown)");
  208. return;
  209. }
  210. char symbolBuffer[512];
  211. if (bufMod)
  212. {
  213. GetModuleFileName((HMODULE)modBase,symbolBuffer,sizeof(symbolBuffer));
  214. char *p=strrchr(symbolBuffer,'\\'); // use filename only, strip off path
  215. p=p?p+1:symbolBuffer;
  216. strncpy(bufMod,p,sizeMod);
  217. bufMod[sizeMod-1]=0;
  218. }
  219. if (relMod)
  220. *relMod=addr-modBase;
  221. // determine symbol
  222. if (bufSym)
  223. {
  224. PIMAGEHLP_SYMBOL symPtr=(PIMAGEHLP_SYMBOL)symbolBuffer;
  225. memset(symPtr,0,sizeof(symbolBuffer));
  226. symPtr->SizeOfStruct=sizeof(IMAGEHLP_SYMBOL);
  227. symPtr->MaxNameLength=sizeof(symbolBuffer)-sizeof(IMAGEHLP_SYMBOL);
  228. DWORD displacement;
  229. if (gDbg._SymGetSymFromAddr((HANDLE)GetCurrentProcessId(),addr,&displacement,symPtr))
  230. {
  231. strncpy(bufSym,symPtr->Name,sizeSym);
  232. bufSym[sizeSym-1]=0;
  233. if (relSym)
  234. *relSym=displacement;
  235. }
  236. else
  237. strcpy(bufSym,"(unknown)");
  238. }
  239. // and line number
  240. if (bufFile)
  241. {
  242. IMAGEHLP_LINE line;
  243. memset(&line,0,sizeof(line));
  244. line.SizeOfStruct=sizeof(line);
  245. DWORD displacement;
  246. if (!gDbg._SymGetLineFromAddr((HANDLE)GetCurrentProcessId(),addr,&displacement,&line))
  247. strcpy(bufFile,"(unknown)");
  248. else
  249. {
  250. char *p=strrchr(line.FileName,'\\'); // use filename only, strip off path
  251. p=p?p+1:line.FileName;
  252. strncpy(bufFile,p,sizeFile);
  253. bufFile[sizeFile-1]=0;
  254. if (linePtr)
  255. *linePtr=line.LineNumber;
  256. if (relLine)
  257. *relLine=displacement;
  258. }
  259. }
  260. }
  261. Debug& operator<<(Debug &dbg, const DebugStackwalk::Signature &sig)
  262. {
  263. dbg << sig.Size() << " addresses:\n";
  264. for (unsigned k=0;k<sig.Size();k++)
  265. {
  266. char buf[512];
  267. sig.GetSymbol(sig.GetAddress(k),buf,sizeof(buf));
  268. dbg << buf << "\n";
  269. }
  270. return dbg;
  271. }
  272. //////////////////////////////////////////////////////////////////////////////
  273. DebugStackwalk::DebugStackwalk(void)
  274. {
  275. // it doesn't harm to do this here
  276. InitDbghelp();
  277. }
  278. DebugStackwalk::~DebugStackwalk()
  279. {
  280. }
  281. void *DebugStackwalk::GetDbghelpHandle(void)
  282. {
  283. return g_dbghelp;
  284. }
  285. bool DebugStackwalk::IsOldDbghelp(void)
  286. {
  287. return g_oldDbghelp;
  288. }
  289. int DebugStackwalk::StackWalk(Signature &sig, struct _CONTEXT *ctx)
  290. {
  291. InitDbghelp();
  292. sig.m_numAddr=0;
  293. // bail out if no stack walk available
  294. if (!gDbg._StackWalk)
  295. return 0;
  296. // Set up the stack frame structure for the start point of the stack walk (i.e. here).
  297. STACKFRAME stackFrame;
  298. memset(&stackFrame,0,sizeof(stackFrame));
  299. stackFrame.AddrPC.Mode = AddrModeFlat;
  300. stackFrame.AddrStack.Mode = AddrModeFlat;
  301. stackFrame.AddrFrame.Mode = AddrModeFlat;
  302. // Use the context struct if it was provided.
  303. if (ctx)
  304. {
  305. stackFrame.AddrPC.Offset = ctx->Eip;
  306. stackFrame.AddrStack.Offset = ctx->Esp;
  307. stackFrame.AddrFrame.Offset = ctx->Ebp;
  308. }
  309. else
  310. {
  311. // walk stack back using current call chain
  312. unsigned long reg_eip, reg_ebp, reg_esp;
  313. __asm
  314. {
  315. here:
  316. lea eax,here
  317. mov reg_eip,eax
  318. mov reg_ebp,ebp
  319. mov reg_esp,esp
  320. };
  321. stackFrame.AddrPC.Offset = reg_eip;
  322. stackFrame.AddrStack.Offset = reg_esp;
  323. stackFrame.AddrFrame.Offset = reg_ebp;
  324. }
  325. // Walk the stack by the requested number of return address iterations.
  326. bool skipFirst=!ctx;
  327. while (sig.m_numAddr<Signature::MAX_ADDR&&
  328. gDbg._StackWalk(IMAGE_FILE_MACHINE_I386,GetCurrentProcess(),GetCurrentThread(),
  329. &stackFrame,NULL,NULL,gDbg._SymFunctionTableAccess,gDbg._SymGetModuleBase,NULL))
  330. {
  331. if (skipFirst)
  332. skipFirst=false;
  333. else
  334. sig.m_addr[sig.m_numAddr++]=stackFrame.AddrPC.Offset;
  335. }
  336. return sig.m_numAddr;
  337. }