Richard Boegli's CnC_Generals_Zero_Hour Fork WIP
This is documentation of Richard Boegil's Zero Hour Fork
 
Loading...
Searching...
No Matches
debug_stack.cpp
Go to the documentation of this file.
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
20// $File: //depot/GeneralsMD/Staging/code/Libraries/Source/debug/debug_stack.cpp $
21// $Author: KMorness $
22// $Revision: #2 $
23// $DateTime: 2005/01/19 15:02:33 $
24//
25// ©2003 Electronic Arts
26//
27// Stack walker
29#include "_pch.h"
30#include "dbghelp.h"
31
32// Definitions to allow run-time linking to the dbghelp.dll functions.
33
34#define DBGHELP(name,ret,par) typedef ret (WINAPI *name##Type) par;
35#include "debug_stack.inl"
36#undef DBGHELP
38#define DBGHELP(name,ret,par) name##Type _##name;
39static union
41 struct
42 {
43#include "debug_stack.inl"
44 };
45 unsigned funcPtr[1];
46} gDbg;
47#undef DBGHELP
49#define DBGHELP(name,ret,par) #name,
50static char const *DebughelpFunctionNames[] =
51{
52#include "debug_stack.inl"
53 NULL
54};
55#undef DBGHELP
56
57// local dbghelp.dll module handle
58static HMODULE g_dbghelp;
59
60// local flag that is true if we're using an old dbghelp.dll version
61static bool g_oldDbghelp;
62
63static void InitDbghelp(void)
64{
65 // already called?
66 if (g_dbghelp)
67 return;
68
69 // firstly check for dbghelp.dll in the EXE directory
70 char dbgHelpPath[256];
71 if (GetModuleFileName(NULL,dbgHelpPath,sizeof(dbgHelpPath)))
72 {
73 char *slash=strrchr(dbgHelpPath,'\\');
74 if (slash)
75 {
76 strcpy(slash+1,"DBGHELP.DLL");
77 g_dbghelp=::LoadLibrary(dbgHelpPath);
78 }
79 }
80 if (!g_dbghelp)
81 // load any version we can
82 g_dbghelp=::LoadLibrary("DBGHELP.DLL");
83
84 if (!g_dbghelp)
85 return;
86
87 // Get function addresses
88 unsigned *funcptr=gDbg.funcPtr;
89 for (unsigned k=0;DebughelpFunctionNames[k];++k,++funcptr)
90 {
91 *funcptr=(unsigned)GetProcAddress(g_dbghelp,DebughelpFunctionNames[k]);
92 if (!*funcptr)
93 break;
94 }
95 if (DebughelpFunctionNames[k])
96 {
97 // not all functions found -> clear them all
98 while (funcptr!=gDbg.funcPtr)
99 *--funcptr=NULL;
100 }
101 else
102 {
103 // Set options
104 gDbg._SymSetOptions(gDbg._SymGetOptions()|SYMOPT_DEFERRED_LOADS|SYMOPT_LOAD_LINES);
105
106 // Init module
107 gDbg._SymInitialize((HANDLE)GetCurrentProcessId(),NULL,TRUE);
108
109 // Check: are we using a newer version of dbghelp.dll?
110 // (older versions have some serious issues.. err... bugs)
111 if (!GetProcAddress(g_dbghelp,"SymEnumSymbolsForAddr"))
112 g_oldDbghelp=true;
113 }
114}
115
117
119{
120 *this=src;
121}
122
124{
125 if (&src!=this)
126 {
127 m_numAddr=src.m_numAddr;
128 memcpy(m_addr,src.m_addr,m_numAddr*sizeof(*m_addr));
129 }
130 return *this;
131}
132
134{
135 DFAIL_IF_MSG(n<0||n>=MAX_ADDR,n << "/" << MAX_ADDR) return 0;
136 return m_addr[n];
137}
138
139void DebugStackwalk::Signature::GetSymbol(unsigned addr, char *buf, unsigned bufSize)
140{
141 DFAIL_IF(!buf) return;
142 DFAIL_IF(bufSize<64||bufSize>=0x80000000) return;
143
144 InitDbghelp();
145
146 char *bufEnd=buf+bufSize;
147 *buf=0;
148 buf+=wsprintf(buf,"%08x",addr);
149
150 // determine module
151 unsigned modBase=gDbg._SymGetModuleBase((HANDLE)GetCurrentProcessId(),addr);
152 if (!modBase)
153 {
154 strcpy(buf," (unknown module)");
155 return;
156 }
157
158 // illegal code ptr?
159 if (IsBadReadPtr((void *)addr,4)||IsBadCodePtr((FARPROC)addr))
160 {
161 strcpy(buf," (invalid code addr)");
162 return;
163 }
164
165 char symbolBuffer[512];
166 GetModuleFileName((HMODULE)modBase,symbolBuffer,sizeof(symbolBuffer));
167
168 char *p=strrchr(symbolBuffer,'\\'); // use filename only, strip off path
169 p=p?p+1:symbolBuffer;
170 *buf++=' ';
171 strcpy(buf,p);
172 buf+=strlen(buf);
173 if (bufEnd-buf<32)
174 return;
175 buf+=wsprintf(buf,"+0x%x",addr-modBase);
176
177 // determine symbol
178 PIMAGEHLP_SYMBOL symPtr=(PIMAGEHLP_SYMBOL)symbolBuffer;
179 memset(symPtr,0,sizeof(symbolBuffer));
180 symPtr->SizeOfStruct=sizeof(IMAGEHLP_SYMBOL);
181 symPtr->MaxNameLength=sizeof(symbolBuffer)-sizeof(IMAGEHLP_SYMBOL);
182 DWORD displacement;
183 if (!gDbg._SymGetSymFromAddr((HANDLE)GetCurrentProcessId(),addr,&displacement,symPtr))
184 return;
185 if ((unsigned int)(bufEnd-buf)<strlen(symPtr->Name)+16)
186 return;
187 buf+=wsprintf(buf,", %s+0x%x",symPtr->Name,displacement);
188
189 // and line number
190 IMAGEHLP_LINE line;
191 memset(&line,0,sizeof(line));
192 line.SizeOfStruct=sizeof(line);
193 if (!gDbg._SymGetLineFromAddr((HANDLE)GetCurrentProcessId(),addr,&displacement,&line))
194 return;
195
196 p=strrchr(line.FileName,'\\'); // use filename only, strip off path
197 p=p?p+1:line.FileName;
198
199 if ((unsigned int)(bufEnd-buf)<strlen(p)+16)
200 return;
201 buf+=wsprintf(buf,", %s:%i+0x%x",p,line.LineNumber,displacement);
202}
203
205 char *bufMod, unsigned sizeMod, unsigned *relMod,
206 char *bufSym, unsigned sizeSym, unsigned *relSym,
207 char *bufFile, unsigned sizeFile, unsigned *linePtr, unsigned *relLine)
208{
209 InitDbghelp();
210
211 if (bufMod) *bufMod=0;
212 if (relMod) *relMod=0;
213 if (bufSym) *bufSym=0;
214 if (relSym) *relSym=0;
215
216 if (bufFile) *bufFile=0;
217 if (linePtr) *linePtr=0;
218 if (relLine) *relLine=0;
219
220 DFAIL_IF(bufMod&&sizeMod<16) return;
221 DFAIL_IF(bufSym&&sizeSym<16) return;
222 DFAIL_IF(bufFile&&sizeFile<16) return;
223
224 // determine module
225 unsigned modBase=gDbg._SymGetModuleBase((HANDLE)GetCurrentProcessId(),addr);
226 if (!modBase)
227 {
228 if (bufMod)
229 strcpy(bufMod,"(unknown mod)");
230 if (bufSym)
231 strcpy(bufSym,"(unknown)");
232 return;
233 }
234
235 // illegal code ptr?
236 if (IsBadReadPtr((void *)addr,4)||IsBadCodePtr((FARPROC)addr))
237 {
238 if (bufMod)
239 strcpy(bufMod,"(inv code addr)");
240 if (bufSym)
241 strcpy(bufSym,"(unknown)");
242 return;
243 }
244
245 char symbolBuffer[512];
246 if (bufMod)
247 {
248 GetModuleFileName((HMODULE)modBase,symbolBuffer,sizeof(symbolBuffer));
249
250 char *p=strrchr(symbolBuffer,'\\'); // use filename only, strip off path
251 p=p?p+1:symbolBuffer;
252 strncpy(bufMod,p,sizeMod);
253 bufMod[sizeMod-1]=0;
254 }
255 if (relMod)
256 *relMod=addr-modBase;
257
258 // determine symbol
259 if (bufSym)
260 {
261 PIMAGEHLP_SYMBOL symPtr=(PIMAGEHLP_SYMBOL)symbolBuffer;
262 memset(symPtr,0,sizeof(symbolBuffer));
263 symPtr->SizeOfStruct=sizeof(IMAGEHLP_SYMBOL);
264 symPtr->MaxNameLength=sizeof(symbolBuffer)-sizeof(IMAGEHLP_SYMBOL);
265 DWORD displacement;
266 if (gDbg._SymGetSymFromAddr((HANDLE)GetCurrentProcessId(),addr,&displacement,symPtr))
267 {
268 strncpy(bufSym,symPtr->Name,sizeSym);
269 bufSym[sizeSym-1]=0;
270 if (relSym)
271 *relSym=displacement;
272 }
273 else
274 strcpy(bufSym,"(unknown)");
275 }
276
277 // and line number
278 if (bufFile)
279 {
280 IMAGEHLP_LINE line;
281 memset(&line,0,sizeof(line));
282 line.SizeOfStruct=sizeof(line);
283 DWORD displacement;
284 if (!gDbg._SymGetLineFromAddr((HANDLE)GetCurrentProcessId(),addr,&displacement,&line))
285 strcpy(bufFile,"(unknown)");
286 else
287 {
288 char *p=strrchr(line.FileName,'\\'); // use filename only, strip off path
289 p=p?p+1:line.FileName;
290 strncpy(bufFile,p,sizeFile);
291 bufFile[sizeFile-1]=0;
292 if (linePtr)
293 *linePtr=line.LineNumber;
294 if (relLine)
295 *relLine=displacement;
296 }
297 }
298}
299
301{
302 dbg << sig.Size() << " addresses:\n";
303
304 for (unsigned k=0;k<sig.Size();k++)
305 {
306 char buf[512];
307 sig.GetSymbol(sig.GetAddress(k),buf,sizeof(buf));
308 dbg << buf << "\n";
309 }
310
311 return dbg;
312}
313
315
316DebugStackwalk::DebugStackwalk(void)
317{
318 // it doesn't harm to do this here
319 InitDbghelp();
320}
321
322DebugStackwalk::~DebugStackwalk()
323{
324}
325
327{
328 return g_dbghelp;
329}
330
332{
333 return g_oldDbghelp;
334}
335
336int DebugStackwalk::StackWalk(Signature &sig, struct _CONTEXT *ctx)
337{
338 InitDbghelp();
339
340 sig.m_numAddr=0;
341
342 // bail out if no stack walk available
343 if (!gDbg._StackWalk)
344 return 0;
345
346 // Set up the stack frame structure for the start point of the stack walk (i.e. here).
347 STACKFRAME stackFrame;
348 memset(&stackFrame,0,sizeof(stackFrame));
349
350 stackFrame.AddrPC.Mode = AddrModeFlat;
351 stackFrame.AddrStack.Mode = AddrModeFlat;
352 stackFrame.AddrFrame.Mode = AddrModeFlat;
353
354 // Use the context struct if it was provided.
355 if (ctx)
356 {
357 stackFrame.AddrPC.Offset = ctx->Eip;
358 stackFrame.AddrStack.Offset = ctx->Esp;
359 stackFrame.AddrFrame.Offset = ctx->Ebp;
360 }
361 else
362 {
363 // walk stack back using current call chain
364 unsigned long reg_eip, reg_ebp, reg_esp;
365 __asm
366 {
367 here:
368 lea eax,here
369 mov reg_eip,eax
370 mov reg_ebp,ebp
371 mov reg_esp,esp
372 };
373 stackFrame.AddrPC.Offset = reg_eip;
374 stackFrame.AddrStack.Offset = reg_esp;
375 stackFrame.AddrFrame.Offset = reg_ebp;
376 }
377
378 // Walk the stack by the requested number of return address iterations.
379 bool skipFirst=!ctx;
380 while (sig.m_numAddr<Signature::MAX_ADDR&&
381 gDbg._StackWalk(IMAGE_FILE_MACHINE_I386,GetCurrentProcess(),GetCurrentThread(),
382 &stackFrame,NULL,NULL,gDbg._SymFunctionTableAccess,gDbg._SymGetModuleBase,NULL))
383 {
384 if (skipFirst)
385 skipFirst=false;
386 else
387 sig.m_addr[sig.m_numAddr++]=stackFrame.AddrPC.Offset;
388 }
389
390 return sig.m_numAddr;
391}
#define NULL
Definition BaseType.h:92
#define TRUE
Definition BaseType.h:109
unsigned long DWORD
Definition bittype.h:57
Debug module main class (singleton).
Definition debug_debug.h:45
a stack trace signature
Definition debug_stack.h:51
static void GetSymbol(unsigned addr, char *buf, unsigned bufSize)
Determines symbol for given address.
Signature & operator=(const Signature &src)
unsigned GetAddress(int n) const
Get a single address from the signature.
static int StackWalk(Signature &sig, struct _CONTEXT *ctx=0)
Walks the stack from the given address.
static void * GetDbghelpHandle(void)
static bool IsOldDbghelp(void)
#define DFAIL_IF_MSG(cond, msg)
#define DFAIL_IF(cond)
unsigned funcPtr[1]
Debug & operator<<(Debug &dbg, const DebugStackwalk::Signature &sig)
Dumps a complete signature with symbols.