shell32: Improve some FIXMEs.
[wine/multimedia.git] / programs / winedbg / info.c
blobe501f43e6a45d7fae7c23242e5185bacd280350c
1 /*
2 * Wine debugger utility routines
4 * Copyright 1993 Eric Youngdale
5 * Copyright 1995 Alexandre Julliard
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
22 #include "config.h"
24 #include <stdlib.h>
25 #include <stdio.h>
26 #include <string.h>
27 #include <stdarg.h>
29 #include "debugger.h"
30 #include "wingdi.h"
31 #include "winuser.h"
32 #include "tlhelp32.h"
33 #include "wine/debug.h"
35 WINE_DEFAULT_DEBUG_CHANNEL(winedbg);
37 /***********************************************************************
38 * print_help
40 * Implementation of the 'help' command.
42 void print_help(void)
44 int i = 0;
45 static const char * const helptext[] =
47 "The commands accepted by the Wine debugger are a reasonable",
48 "subset of the commands that gdb accepts.",
49 "The commands currently are:",
50 " help quit",
51 " break [*<addr>] watch *<addr>",
52 " delete break bpnum disable bpnum",
53 " enable bpnum condition <bpnum> [<expr>]",
54 " finish cont [N]",
55 " step [N] next [N]",
56 " stepi [N] nexti [N]",
57 " x <addr> print <expr>",
58 " display <expr> undisplay <disnum>",
59 " local display <expr> delete display <disnum>",
60 " enable display <disnum> disable display <disnum>",
61 " bt [<tid>|all] frame <n>",
62 " up down",
63 " list <lines> disassemble [<addr>][,<addr>]",
64 " show dir dir <path>",
65 " set <reg> = <expr> set *<addr> = <expr>",
66 " pass whatis",
67 " info (see 'help info' for options)",
69 "The 'x' command accepts repeat counts and formats (including 'i') in the",
70 "same way that gdb does.\n",
72 "The following are examples of legal expressions:",
73 " $eax $eax+0x3 0x1000 ($eip + 256) *$eax *($esp + 3)",
74 " Also, a nm format symbol table can be read from a file using the",
75 " symbolfile command.", /* Symbols can also be defined individually with",
76 " the define command.", */
77 "",
78 NULL
81 while (helptext[i]) dbg_printf("%s\n", helptext[i++]);
85 /***********************************************************************
86 * info_help
88 * Implementation of the 'help info' command.
90 void info_help(void)
92 int i = 0;
93 static const char * const infotext[] =
95 "The info commands allow you to get assorted bits of interesting stuff",
96 "to be displayed. The options are:",
97 " info break Displays information about breakpoints",
98 " info class <name> Displays information about window class <name>",
99 " info display Shows auto-display expressions in use",
100 " info except <pid> Shows exception handler chain (in a given process)",
101 " info locals Displays values of all local vars for current frame",
102 " info maps <pid> Shows virtual mappings (in a given process)",
103 " info process Shows all running processes",
104 " info reg Displays values of the general registers at top of stack",
105 " info all-reg Displays the general and floating point registers",
106 " info segments <pid> Displays information about all known segments",
107 " info share Displays all loaded modules",
108 " info share <addr> Displays internal module state",
109 " info stack Dumps information about top of stack",
110 " info symbol <sym> Displays information about a given symbol",
111 " info thread Shows all running threads",
112 " info wnd <handle> Displays internal window state",
114 NULL
117 while (infotext[i]) dbg_printf("%s\n", infotext[i++]);
120 static const char* get_symtype_str(const IMAGEHLP_MODULE64* mi)
122 switch (mi->SymType)
124 default:
125 case SymNone: return "--none--";
126 case SymCoff: return "COFF";
127 case SymCv: return "CodeView";
128 case SymPdb: return "PDB";
129 case SymExport: return "Export";
130 case SymDeferred: return "Deferred";
131 case SymSym: return "Sym";
132 case SymDia:
133 switch (mi->CVSig)
135 case 'S' | ('T' << 8) | ('A' << 16) | ('B' << 24):
136 return "Stabs";
137 case 'D' | ('W' << 8) | ('A' << 16) | ('R' << 24):
138 return "Dwarf";
139 default:
140 return "DIA";
146 struct info_module
148 IMAGEHLP_MODULE64* mi;
149 unsigned num_alloc;
150 unsigned num_used;
153 static void module_print_info(const IMAGEHLP_MODULE64* mi, BOOL is_embedded)
155 dbg_printf("%*.*s-%*.*s\t%-16s%s\n",
156 ADDRWIDTH, ADDRWIDTH, wine_dbgstr_longlong(mi->BaseOfImage),
157 ADDRWIDTH, ADDRWIDTH, wine_dbgstr_longlong(mi->BaseOfImage + mi->ImageSize),
158 is_embedded ? "\\" : get_symtype_str(mi), mi->ModuleName);
161 static int module_compare(const void* p1, const void* p2)
163 LONGLONG val = ((const IMAGEHLP_MODULE64*)p1)->BaseOfImage -
164 ((const IMAGEHLP_MODULE64*)p2)->BaseOfImage;
165 if (val < 0) return -1;
166 else if (val > 0) return 1;
167 else return 0;
170 static inline BOOL module_is_container(const IMAGEHLP_MODULE64* wmod_cntnr,
171 const IMAGEHLP_MODULE64* wmod_child)
173 return wmod_cntnr->BaseOfImage <= wmod_child->BaseOfImage &&
174 wmod_cntnr->BaseOfImage + wmod_cntnr->ImageSize >=
175 wmod_child->BaseOfImage + wmod_child->ImageSize;
178 static BOOL CALLBACK info_mod_cb(PCSTR mod_name, DWORD64 base, PVOID ctx)
180 struct info_module* im = ctx;
182 if (im->num_used + 1 > im->num_alloc)
184 im->num_alloc += 16;
185 im->mi = dbg_heap_realloc(im->mi, im->num_alloc * sizeof(*im->mi));
187 im->mi[im->num_used].SizeOfStruct = sizeof(im->mi[im->num_used]);
188 if (SymGetModuleInfo64(dbg_curr_process->handle, base, &im->mi[im->num_used]))
190 im->num_used++;
192 return TRUE;
195 /***********************************************************************
196 * info_win32_module
198 * Display information about a given module (DLL or EXE), or about all modules
200 void info_win32_module(DWORD64 base)
202 struct info_module im;
203 UINT i, j, num_printed = 0;
204 DWORD opt;
206 if (!dbg_curr_process)
208 dbg_printf("Cannot get info on module while no process is loaded\n");
209 return;
212 im.mi = NULL;
213 im.num_alloc = im.num_used = 0;
215 /* this is a wine specific options to return also ELF modules in the
216 * enumeration
218 SymSetOptions((opt = SymGetOptions()) | 0x40000000);
219 SymEnumerateModules64(dbg_curr_process->handle, info_mod_cb, (void*)&im);
220 SymSetOptions(opt);
222 qsort(im.mi, im.num_used, sizeof(im.mi[0]), module_compare);
224 dbg_printf("Module\tAddress\t\t\t%sDebug info\tName (%d modules)\n",
225 ADDRWIDTH == 16 ? "\t\t" : "", im.num_used);
227 for (i = 0; i < im.num_used; i++)
229 if (base &&
230 (base < im.mi[i].BaseOfImage || base >= im.mi[i].BaseOfImage + im.mi[i].ImageSize))
231 continue;
232 if (strstr(im.mi[i].ModuleName, "<elf>"))
234 dbg_printf("ELF\t");
235 module_print_info(&im.mi[i], FALSE);
236 /* print all modules embedded in this one */
237 for (j = 0; j < im.num_used; j++)
239 if (!strstr(im.mi[j].ModuleName, "<elf>") && module_is_container(&im.mi[i], &im.mi[j]))
241 dbg_printf(" \\-PE\t");
242 module_print_info(&im.mi[j], TRUE);
246 else
248 /* check module is not embedded in another module */
249 for (j = 0; j < im.num_used; j++)
251 if (strstr(im.mi[j].ModuleName, "<elf>") && module_is_container(&im.mi[j], &im.mi[i]))
252 break;
254 if (j < im.num_used) continue;
255 if (strstr(im.mi[i].ModuleName, ".so") || strchr(im.mi[i].ModuleName, '<'))
256 dbg_printf("ELF\t");
257 else
258 dbg_printf("PE\t");
259 module_print_info(&im.mi[i], FALSE);
261 num_printed++;
263 HeapFree(GetProcessHeap(), 0, im.mi);
265 if (base && !num_printed)
266 dbg_printf("'0x%x%08x' is not a valid module address\n", (DWORD)(base >> 32), (DWORD)base);
269 struct class_walker
271 ATOM* table;
272 int used;
273 int alloc;
276 static void class_walker(HWND hWnd, struct class_walker* cw)
278 char clsName[128];
279 int i;
280 ATOM atom;
281 HWND child;
283 if (!GetClassNameA(hWnd, clsName, sizeof(clsName)))
284 return;
285 if ((atom = FindAtomA(clsName)) == 0)
286 return;
288 for (i = 0; i < cw->used; i++)
290 if (cw->table[i] == atom)
291 break;
293 if (i == cw->used)
295 if (cw->used >= cw->alloc)
297 cw->alloc += 16;
298 cw->table = dbg_heap_realloc(cw->table, cw->alloc * sizeof(ATOM));
300 cw->table[cw->used++] = atom;
301 info_win32_class(hWnd, clsName);
305 if ((child = GetWindow(hWnd, GW_CHILD)) != 0)
306 class_walker(child, cw);
307 } while ((hWnd = GetWindow(hWnd, GW_HWNDNEXT)) != 0);
310 void info_win32_class(HWND hWnd, const char* name)
312 WNDCLASSEXA wca;
313 HINSTANCE hInst = hWnd ? (HINSTANCE)GetWindowLongPtrW(hWnd, GWLP_HINSTANCE) : 0;
315 if (!name)
317 struct class_walker cw;
319 cw.table = NULL;
320 cw.used = cw.alloc = 0;
321 class_walker(GetDesktopWindow(), &cw);
322 HeapFree(GetProcessHeap(), 0, cw.table);
323 return;
326 if (!GetClassInfoExA(hInst, name, &wca))
328 dbg_printf("Cannot find class '%s'\n", name);
329 return;
332 dbg_printf("Class '%s':\n", name);
333 dbg_printf("style=0x%08x wndProc=%p\n"
334 "inst=%p icon=%p cursor=%p bkgnd=%p\n"
335 "clsExtra=%d winExtra=%d\n",
336 wca.style, wca.lpfnWndProc, wca.hInstance,
337 wca.hIcon, wca.hCursor, wca.hbrBackground,
338 wca.cbClsExtra, wca.cbWndExtra);
340 if (hWnd && wca.cbClsExtra)
342 int i;
343 WORD w;
345 dbg_printf("Extra bytes:");
346 for (i = 0; i < wca.cbClsExtra / 2; i++)
348 w = GetClassWord(hWnd, i * 2);
349 /* FIXME: depends on i386 endian-ity */
350 dbg_printf(" %02x %02x", HIBYTE(w), LOBYTE(w));
352 dbg_printf("\n");
354 dbg_printf("\n");
355 /* FIXME:
356 * + print #windows (or even list of windows...)
357 * + print extra bytes => this requires a window handle on this very class...
361 static void info_window(HWND hWnd, int indent)
363 char clsName[128];
364 char wndName[128];
365 HWND child;
369 if (!GetClassNameA(hWnd, clsName, sizeof(clsName)))
370 strcpy(clsName, "-- Unknown --");
371 if (!GetWindowTextA(hWnd, wndName, sizeof(wndName)))
372 strcpy(wndName, "-- Empty --");
374 dbg_printf("%*s%08lx%*s %-17.17s %08x %0*lx %08x %.14s\n",
375 indent, "", (DWORD_PTR)hWnd, 12 - indent, "",
376 clsName, GetWindowLongW(hWnd, GWL_STYLE),
377 ADDRWIDTH, (ULONG_PTR)GetWindowLongPtrW(hWnd, GWLP_WNDPROC),
378 GetWindowThreadProcessId(hWnd, NULL), wndName);
380 if ((child = GetWindow(hWnd, GW_CHILD)) != 0)
381 info_window(child, indent + 1);
382 } while ((hWnd = GetWindow(hWnd, GW_HWNDNEXT)) != 0);
385 void info_win32_window(HWND hWnd, BOOL detailed)
387 char clsName[128];
388 char wndName[128];
389 RECT clientRect;
390 RECT windowRect;
391 WORD w;
393 if (!IsWindow(hWnd)) hWnd = GetDesktopWindow();
395 if (!detailed)
397 dbg_printf("%-20.20s %-17.17s %-8.8s %-*.*s %-8.8s %s\n",
398 "Window handle", "Class Name", "Style",
399 ADDRWIDTH, ADDRWIDTH, "WndProc", "Thread", "Text");
400 info_window(hWnd, 0);
401 return;
404 if (!GetClassNameA(hWnd, clsName, sizeof(clsName)))
405 strcpy(clsName, "-- Unknown --");
406 if (!GetWindowTextA(hWnd, wndName, sizeof(wndName)))
407 strcpy(wndName, "-- Empty --");
408 if (!GetClientRect(hWnd, &clientRect) ||
409 !MapWindowPoints(hWnd, 0, (LPPOINT) &clientRect, 2))
410 SetRectEmpty(&clientRect);
411 if (!GetWindowRect(hWnd, &windowRect))
412 SetRectEmpty(&windowRect);
414 /* FIXME missing fields: hmemTaskQ, hrgnUpdate, dce, flags, pProp, scroll */
415 dbg_printf("next=%p child=%p parent=%p owner=%p class='%s'\n"
416 "inst=%p active=%p idmenu=%08lx\n"
417 "style=0x%08x exstyle=0x%08x wndproc=%p text='%s'\n"
418 "client=%d,%d-%d,%d window=%d,%d-%d,%d sysmenu=%p\n",
419 GetWindow(hWnd, GW_HWNDNEXT),
420 GetWindow(hWnd, GW_CHILD),
421 GetParent(hWnd),
422 GetWindow(hWnd, GW_OWNER),
423 clsName,
424 (HINSTANCE)GetWindowLongPtrW(hWnd, GWLP_HINSTANCE),
425 GetLastActivePopup(hWnd),
426 (ULONG_PTR)GetWindowLongPtrW(hWnd, GWLP_ID),
427 GetWindowLongW(hWnd, GWL_STYLE),
428 GetWindowLongW(hWnd, GWL_EXSTYLE),
429 (void*)GetWindowLongPtrW(hWnd, GWLP_WNDPROC),
430 wndName,
431 clientRect.left, clientRect.top, clientRect.right, clientRect.bottom,
432 windowRect.left, windowRect.top, windowRect.right, windowRect.bottom,
433 GetSystemMenu(hWnd, FALSE));
435 if (GetClassLongW(hWnd, GCL_CBWNDEXTRA))
437 UINT i;
439 dbg_printf("Extra bytes:");
440 for (i = 0; i < GetClassLongW(hWnd, GCL_CBWNDEXTRA) / 2; i++)
442 w = GetWindowWord(hWnd, i * 2);
443 /* FIXME: depends on i386 endian-ity */
444 dbg_printf(" %02x %02x", HIBYTE(w), LOBYTE(w));
446 dbg_printf("\n");
448 dbg_printf("\n");
451 void info_win32_processes(void)
453 HANDLE snap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
454 if (snap != INVALID_HANDLE_VALUE)
456 PROCESSENTRY32 entry;
457 DWORD current = dbg_curr_process ? dbg_curr_process->pid : 0;
458 BOOL ok;
460 entry.dwSize = sizeof(entry);
461 ok = Process32First(snap, &entry);
463 dbg_printf(" %-8.8s %-8.8s %-8.8s %s (all id:s are in hex)\n",
464 "pid", "threads", "parent", "executable");
465 while (ok)
467 if (entry.th32ProcessID != GetCurrentProcessId())
468 dbg_printf("%c%08x %-8d %08x '%s'\n",
469 (entry.th32ProcessID == current) ? '>' : ' ',
470 entry.th32ProcessID, entry.cntThreads,
471 entry.th32ParentProcessID, entry.szExeFile);
472 ok = Process32Next(snap, &entry);
474 CloseHandle(snap);
478 static BOOL get_process_name(DWORD pid, PROCESSENTRY32* entry)
480 BOOL ret = FALSE;
481 HANDLE snap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
483 if (snap != INVALID_HANDLE_VALUE)
485 entry->dwSize = sizeof(*entry);
486 if (Process32First(snap, entry))
487 while (!(ret = (entry->th32ProcessID == pid)) &&
488 Process32Next(snap, entry));
489 CloseHandle(snap);
491 return ret;
494 void info_win32_threads(void)
496 HANDLE snap = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
497 if (snap != INVALID_HANDLE_VALUE)
499 THREADENTRY32 entry;
500 BOOL ok;
501 DWORD lastProcessId = 0;
503 entry.dwSize = sizeof(entry);
504 ok = Thread32First(snap, &entry);
506 dbg_printf("%-8.8s %-8.8s %s (all id:s are in hex)\n",
507 "process", "tid", "prio");
508 while (ok)
510 if (entry.th32OwnerProcessID != GetCurrentProcessId())
512 /* FIXME: this assumes that, in the snapshot, all threads of a same process are
513 * listed sequentially, which is not specified in the doc (Wine's implementation
514 * does it)
516 if (entry.th32OwnerProcessID != lastProcessId)
518 struct dbg_process* p = dbg_get_process(entry.th32OwnerProcessID);
519 PROCESSENTRY32 pcs_entry;
520 const char* exename;
522 if (p)
523 exename = dbg_W2A(p->imageName, -1);
524 else if (get_process_name(entry.th32OwnerProcessID, &pcs_entry))
525 exename = pcs_entry.szExeFile;
526 else
527 exename = "";
529 dbg_printf("%08x%s %s\n",
530 entry.th32OwnerProcessID, p ? " (D)" : "", exename);
531 lastProcessId = entry.th32OwnerProcessID;
533 dbg_printf("\t%08x %4d%s\n",
534 entry.th32ThreadID, entry.tpBasePri,
535 (entry.th32ThreadID == dbg_curr_tid) ? " <==" : "");
538 ok = Thread32Next(snap, &entry);
541 CloseHandle(snap);
545 /***********************************************************************
546 * info_win32_exceptions
548 * Get info on the exception frames of a given thread.
550 void info_win32_exceptions(DWORD tid)
552 struct dbg_thread* thread;
553 void* next_frame;
555 if (!dbg_curr_process || !dbg_curr_thread)
557 dbg_printf("Cannot get info on exceptions while no process is loaded\n");
558 return;
561 dbg_printf("Exception frames:\n");
563 if (tid == dbg_curr_tid) thread = dbg_curr_thread;
564 else
566 thread = dbg_get_thread(dbg_curr_process, tid);
568 if (!thread)
570 dbg_printf("Unknown thread id (%04x) in current process\n", tid);
571 return;
573 if (SuspendThread(thread->handle) == -1)
575 dbg_printf("Can't suspend thread id (%04x)\n", tid);
576 return;
580 if (!dbg_read_memory(thread->teb, &next_frame, sizeof(next_frame)))
582 dbg_printf("Can't read TEB:except_frame\n");
583 return;
586 while (next_frame != (void*)-1)
588 EXCEPTION_REGISTRATION_RECORD frame;
590 dbg_printf("%p: ", next_frame);
591 if (!dbg_read_memory(next_frame, &frame, sizeof(frame)))
593 dbg_printf("Invalid frame address\n");
594 break;
596 dbg_printf("prev=%p handler=%p\n", frame.Prev, frame.Handler);
597 next_frame = frame.Prev;
600 if (tid != dbg_curr_tid) ResumeThread(thread->handle);
603 void info_win32_segments(DWORD start, int length)
605 char flags[3];
606 DWORD i;
607 LDT_ENTRY le;
609 if (length == -1) length = (8192 - start);
611 for (i = start; i < start + length; i++)
613 if (!dbg_curr_process->process_io->get_selector(dbg_curr_thread->handle, (i << 3) | 7, &le))
614 continue;
616 if (le.HighWord.Bits.Type & 0x08)
618 flags[0] = (le.HighWord.Bits.Type & 0x2) ? 'r' : '-';
619 flags[1] = '-';
620 flags[2] = 'x';
622 else
624 flags[0] = 'r';
625 flags[1] = (le.HighWord.Bits.Type & 0x2) ? 'w' : '-';
626 flags[2] = '-';
628 dbg_printf("%04x: sel=%04x base=%08x limit=%08x %d-bit %c%c%c\n",
629 i, (i << 3) | 7,
630 (le.HighWord.Bits.BaseHi << 24) +
631 (le.HighWord.Bits.BaseMid << 16) + le.BaseLow,
632 ((le.HighWord.Bits.LimitHi << 8) + le.LimitLow) <<
633 (le.HighWord.Bits.Granularity ? 12 : 0),
634 le.HighWord.Bits.Default_Big ? 32 : 16,
635 flags[0], flags[1], flags[2]);
639 void info_win32_virtual(DWORD pid)
641 MEMORY_BASIC_INFORMATION mbi;
642 char* addr = 0;
643 const char* state;
644 const char* type;
645 char prot[3+1];
646 HANDLE hProc;
648 if (pid == dbg_curr_pid)
650 if (dbg_curr_process == NULL)
652 dbg_printf("Cannot look at mapping of current process, while no process is loaded\n");
653 return;
655 hProc = dbg_curr_process->handle;
657 else
659 hProc = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, pid);
660 if (hProc == NULL)
662 dbg_printf("Cannot open process <%04x>\n", pid);
663 return;
667 dbg_printf("Address End State Type RWX\n");
669 while (VirtualQueryEx(hProc, addr, &mbi, sizeof(mbi)) >= sizeof(mbi))
671 switch (mbi.State)
673 case MEM_COMMIT: state = "commit "; break;
674 case MEM_FREE: state = "free "; break;
675 case MEM_RESERVE: state = "reserve"; break;
676 default: state = "??? "; break;
678 if (mbi.State != MEM_FREE)
680 switch (mbi.Type)
682 case MEM_IMAGE: type = "image "; break;
683 case MEM_MAPPED: type = "mapped "; break;
684 case MEM_PRIVATE: type = "private"; break;
685 case 0: type = " "; break;
686 default: type = "??? "; break;
688 memset(prot, ' ' , sizeof(prot) - 1);
689 prot[sizeof(prot) - 1] = '\0';
690 if (mbi.AllocationProtect & (PAGE_READONLY|PAGE_READWRITE|PAGE_EXECUTE_READ|PAGE_EXECUTE_READWRITE))
691 prot[0] = 'R';
692 if (mbi.AllocationProtect & (PAGE_READWRITE|PAGE_EXECUTE_READWRITE))
693 prot[1] = 'W';
694 if (mbi.AllocationProtect & (PAGE_WRITECOPY|PAGE_EXECUTE_WRITECOPY))
695 prot[1] = 'C';
696 if (mbi.AllocationProtect & (PAGE_EXECUTE|PAGE_EXECUTE_READ|PAGE_EXECUTE_READWRITE))
697 prot[2] = 'X';
699 else
701 type = "";
702 prot[0] = '\0';
704 dbg_printf("%08lx %08lx %s %s %s\n",
705 (DWORD_PTR)addr, (DWORD_PTR)addr + mbi.RegionSize - 1, state, type, prot);
706 if (addr + mbi.RegionSize < addr) /* wrap around ? */
707 break;
708 addr += mbi.RegionSize;
710 if (pid != dbg_curr_pid) CloseHandle(hProc);
713 void info_wine_dbg_channel(BOOL turn_on, const char* cls, const char* name)
715 struct dbg_lvalue lvalue;
716 struct __wine_debug_channel channel;
717 unsigned char mask;
718 int done = 0;
719 BOOL bAll;
720 void* addr;
722 if (!dbg_curr_process || !dbg_curr_thread)
724 dbg_printf("Cannot set/get debug channels while no process is loaded\n");
725 return;
728 if (symbol_get_lvalue("debug_options", -1, &lvalue, FALSE) != sglv_found)
730 return;
732 addr = memory_to_linear_addr(&lvalue.addr);
734 if (!cls) mask = ~0;
735 else if (!strcmp(cls, "fixme")) mask = (1 << __WINE_DBCL_FIXME);
736 else if (!strcmp(cls, "err")) mask = (1 << __WINE_DBCL_ERR);
737 else if (!strcmp(cls, "warn")) mask = (1 << __WINE_DBCL_WARN);
738 else if (!strcmp(cls, "trace")) mask = (1 << __WINE_DBCL_TRACE);
739 else
741 dbg_printf("Unknown debug class %s\n", cls);
742 return;
745 bAll = !strcmp("all", name);
746 while (addr && dbg_read_memory(addr, &channel, sizeof(channel)))
748 if (!channel.name[0]) break;
749 if (bAll || !strcmp( channel.name, name ))
751 if (turn_on) channel.flags |= mask;
752 else channel.flags &= ~mask;
753 if (dbg_write_memory(addr, &channel, sizeof(channel))) done++;
755 addr = (struct __wine_debug_channel *)addr + 1;
757 if (!done) dbg_printf("Unable to find debug channel %s\n", name);
758 else WINE_TRACE("Changed %d channel instances\n", done);