diff options
Diffstat (limited to 'linden/indra/newview/llwindebug.cpp')
-rw-r--r-- | linden/indra/newview/llwindebug.cpp | 194 |
1 files changed, 193 insertions, 1 deletions
diff --git a/linden/indra/newview/llwindebug.cpp b/linden/indra/newview/llwindebug.cpp index cf30f34..e6c11d8 100644 --- a/linden/indra/newview/llwindebug.cpp +++ b/linden/indra/newview/llwindebug.cpp | |||
@@ -121,6 +121,172 @@ MODULE32_NEST Module32Next_; | |||
121 | #define CALL_TRACE_MAX ((DUMP_SIZE_MAX - 2000) / (MAX_PATH + 40)) //max number of traced calls | 121 | #define CALL_TRACE_MAX ((DUMP_SIZE_MAX - 2000) / (MAX_PATH + 40)) //max number of traced calls |
122 | #define NL L"\r\n" //new line | 122 | #define NL L"\r\n" //new line |
123 | 123 | ||
124 | BOOL WINAPI Get_Module_By_Ret_Addr(PBYTE Ret_Addr, LPWSTR Module_Name, PBYTE & Module_Addr); | ||
125 | |||
126 | |||
127 | void printError( CHAR* msg ) | ||
128 | { | ||
129 | DWORD eNum; | ||
130 | TCHAR sysMsg[256]; | ||
131 | TCHAR* p; | ||
132 | |||
133 | eNum = GetLastError( ); | ||
134 | FormatMessage( FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, | ||
135 | NULL, eNum, | ||
136 | MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language | ||
137 | sysMsg, 256, NULL ); | ||
138 | |||
139 | // Trim the end of the line and terminate it with a null | ||
140 | p = sysMsg; | ||
141 | while( ( *p > 31 ) || ( *p == 9 ) ) | ||
142 | ++p; | ||
143 | do { *p-- = 0; } while( ( p >= sysMsg ) && | ||
144 | ( ( *p == '.' ) || ( *p < 33 ) ) ); | ||
145 | |||
146 | // Display the message | ||
147 | printf( "\n WARNING: %s failed with error %d (%s)", msg, eNum, sysMsg ); | ||
148 | } | ||
149 | |||
150 | BOOL GetProcessThreadIDs(DWORD process_id, std::vector<DWORD>& thread_ids) | ||
151 | { | ||
152 | HANDLE hThreadSnap = INVALID_HANDLE_VALUE; | ||
153 | THREADENTRY32 te32; | ||
154 | |||
155 | // Take a snapshot of all running threads | ||
156 | hThreadSnap = CreateToolhelp32Snapshot( TH32CS_SNAPTHREAD, 0 ); | ||
157 | if( hThreadSnap == INVALID_HANDLE_VALUE ) | ||
158 | return( FALSE ); | ||
159 | |||
160 | // Fill in the size of the structure before using it. | ||
161 | te32.dwSize = sizeof(THREADENTRY32 ); | ||
162 | |||
163 | // Retrieve information about the first thread, | ||
164 | // and exit if unsuccessful | ||
165 | if( !Thread32First( hThreadSnap, &te32 ) ) | ||
166 | { | ||
167 | printError( "Thread32First" ); // Show cause of failure | ||
168 | CloseHandle( hThreadSnap ); // Must clean up the snapshot object! | ||
169 | return( FALSE ); | ||
170 | } | ||
171 | |||
172 | // Now walk the thread list of the system, | ||
173 | // and display information about each thread | ||
174 | // associated with the specified process | ||
175 | do | ||
176 | { | ||
177 | if( te32.th32OwnerProcessID == process_id ) | ||
178 | { | ||
179 | thread_ids.push_back(te32.th32ThreadID); | ||
180 | } | ||
181 | } while( Thread32Next(hThreadSnap, &te32 ) ); | ||
182 | |||
183 | // Don't forget to clean up the snapshot object. | ||
184 | CloseHandle( hThreadSnap ); | ||
185 | return( TRUE ); | ||
186 | } | ||
187 | |||
188 | void WINAPI GetCallStackData(const CONTEXT* context_struct, LLSD& info) | ||
189 | { | ||
190 | // Fill Str with call stack info. | ||
191 | // pException can be either GetExceptionInformation() or NULL. | ||
192 | // If pException = NULL - get current call stack. | ||
193 | |||
194 | LPWSTR Module_Name = new WCHAR[MAX_PATH]; | ||
195 | PBYTE Module_Addr = 0; | ||
196 | |||
197 | typedef struct STACK | ||
198 | { | ||
199 | STACK * Ebp; | ||
200 | PBYTE Ret_Addr; | ||
201 | DWORD Param[0]; | ||
202 | } STACK, * PSTACK; | ||
203 | |||
204 | PSTACK Ebp; | ||
205 | |||
206 | if(context_struct) | ||
207 | { | ||
208 | Ebp = (PSTACK)context_struct->Ebp; | ||
209 | } | ||
210 | else | ||
211 | { | ||
212 | // The context struct is NULL, | ||
213 | // so we will use the current stack. | ||
214 | Ebp = (PSTACK)&context_struct - 1; | ||
215 | |||
216 | // Skip frame of GetCallStackData(). | ||
217 | if (!IsBadReadPtr(Ebp, sizeof(PSTACK))) | ||
218 | Ebp = Ebp->Ebp; //caller ebp | ||
219 | } | ||
220 | |||
221 | // Trace CALL_TRACE_MAX calls maximum - not to exceed DUMP_SIZE_MAX. | ||
222 | // Break trace on wrong stack frame. | ||
223 | for (int Ret_Addr_I = 0, i = 0; | ||
224 | (Ret_Addr_I < CALL_TRACE_MAX) && !IsBadReadPtr(Ebp, sizeof(PSTACK)) && !IsBadCodePtr(FARPROC(Ebp->Ret_Addr)); | ||
225 | Ret_Addr_I++, Ebp = Ebp->Ebp, ++i) | ||
226 | { | ||
227 | // If module with Ebp->Ret_Addr found. | ||
228 | |||
229 | if (Get_Module_By_Ret_Addr(Ebp->Ret_Addr, Module_Name, Module_Addr)) | ||
230 | { | ||
231 | // Save module's address and full path. | ||
232 | info["CallStack"][i]["ModuleName"] = ll_convert_wide_to_string(Module_Name); | ||
233 | info["CallStack"][i]["ModuleAddress"] = (int)Module_Addr; | ||
234 | info["CallStack"][i]["CallOffset"] = (int)(Ebp->Ret_Addr - Module_Addr); | ||
235 | |||
236 | LLSD params; | ||
237 | // Save 5 params of the call. We don't know the real number of params. | ||
238 | if (!IsBadReadPtr(Ebp, sizeof(PSTACK) + 5 * sizeof(DWORD))) | ||
239 | { | ||
240 | for(int j = 0; j < 5; ++j) | ||
241 | { | ||
242 | params[j] = (int)Ebp->Param[j]; | ||
243 | } | ||
244 | } | ||
245 | info["CallStack"][i]["Parameters"] = params; | ||
246 | } | ||
247 | info["CallStack"][i]["ReturnAddress"] = (int)Ebp->Ret_Addr; | ||
248 | } | ||
249 | } | ||
250 | |||
251 | BOOL GetThreadCallStack(DWORD thread_id, LLSD& info) | ||
252 | { | ||
253 | if(GetCurrentThreadId() == thread_id) | ||
254 | { | ||
255 | // Early exit for the current thread. | ||
256 | // Suspending the current thread would be a bad idea. | ||
257 | // Plus you can't retrieve a valid current thread context. | ||
258 | return false; | ||
259 | } | ||
260 | |||
261 | HANDLE thread_handle = INVALID_HANDLE_VALUE; | ||
262 | thread_handle = OpenThread(THREAD_ALL_ACCESS, FALSE, thread_id); | ||
263 | if(INVALID_HANDLE_VALUE == thread_handle) | ||
264 | { | ||
265 | return FALSE; | ||
266 | } | ||
267 | |||
268 | BOOL result = false; | ||
269 | if(-1 != SuspendThread(thread_handle)) | ||
270 | { | ||
271 | CONTEXT context_struct; | ||
272 | context_struct.ContextFlags = CONTEXT_FULL; | ||
273 | if(GetThreadContext(thread_handle, &context_struct)) | ||
274 | { | ||
275 | GetCallStackData(&context_struct, info); | ||
276 | result = true; | ||
277 | } | ||
278 | ResumeThread(thread_handle); | ||
279 | } | ||
280 | else | ||
281 | { | ||
282 | // Couldn't suspend thread. | ||
283 | } | ||
284 | |||
285 | CloseHandle(thread_handle); | ||
286 | return result; | ||
287 | } | ||
288 | |||
289 | |||
124 | //Windows Call Stack Construction idea from | 290 | //Windows Call Stack Construction idea from |
125 | //http://www.codeproject.com/tools/minidump.asp | 291 | //http://www.codeproject.com/tools/minidump.asp |
126 | 292 | ||
@@ -490,7 +656,33 @@ LONG LLWinDebug::handleException(struct _EXCEPTION_POINTERS *exception_infop) | |||
490 | 656 | ||
491 | LLSD info; | 657 | LLSD info; |
492 | info = Get_Exception_Info(exception_infop); | 658 | info = Get_Exception_Info(exception_infop); |
493 | if (info) | 659 | |
660 | |||
661 | LLSD threads; | ||
662 | std::vector<DWORD> thread_ids; | ||
663 | GetProcessThreadIDs(GetCurrentProcessId(), thread_ids); | ||
664 | |||
665 | for(std::vector<DWORD>::iterator th_itr = thread_ids.begin(); | ||
666 | th_itr != thread_ids.end(); | ||
667 | ++th_itr) | ||
668 | { | ||
669 | LLSD thread_info; | ||
670 | if(*th_itr != GetCurrentThreadId()) | ||
671 | { | ||
672 | GetThreadCallStack(*th_itr, thread_info); | ||
673 | } | ||
674 | |||
675 | if(thread_info) | ||
676 | { | ||
677 | |||
678 | threads[llformat("ID %d", *th_itr)] = thread_info; | ||
679 | } | ||
680 | } | ||
681 | |||
682 | |||
683 | info["Threads"] = threads; | ||
684 | |||
685 | if (info) | ||
494 | { | 686 | { |
495 | std::ofstream out_file(log_path.c_str()); | 687 | std::ofstream out_file(log_path.c_str()); |
496 | LLSDSerialize::toPrettyXML(info, out_file); | 688 | LLSDSerialize::toPrettyXML(info, out_file); |