push a6a7df41ae272b3b1bf5fcfb544267c1e96afa99
[wine/hacks.git] / programs / winedbg / info.c
blobfd160d77a3032781a5a5646e3610be4b996f4a80
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 DWORD base;
150 unsigned num_alloc;
151 unsigned num_used;
154 static void module_print_info(const IMAGEHLP_MODULE64* mi, BOOL is_embedded)
156 dbg_printf("%8s-%8s\t%-16s%s\n",
157 wine_dbgstr_longlong(mi->BaseOfImage),
158 wine_dbgstr_longlong(mi->BaseOfImage + mi->ImageSize),
159 is_embedded ? "\\" : get_symtype_str(mi), mi->ModuleName);
162 static int module_compare(const void* p1, const void* p2)
164 LONGLONG val = ((const IMAGEHLP_MODULE64*)p1)->BaseOfImage -
165 ((const IMAGEHLP_MODULE64*)p2)->BaseOfImage;
166 if (val < 0) return -1;
167 else if (val > 0) return 1;
168 else return 0;
171 static inline BOOL module_is_container(const IMAGEHLP_MODULE64* wmod_cntnr,
172 const IMAGEHLP_MODULE64* wmod_child)
174 return wmod_cntnr->BaseOfImage <= wmod_child->BaseOfImage &&
175 wmod_cntnr->BaseOfImage + wmod_cntnr->ImageSize >=
176 wmod_child->BaseOfImage + wmod_child->ImageSize;
179 static BOOL CALLBACK info_mod_cb(PCSTR mod_name, DWORD64 base, PVOID ctx)
181 struct info_module* im = (struct info_module*)ctx;
183 if (im->num_used + 1 > im->num_alloc)
185 im->num_alloc += 16;
186 im->mi = dbg_heap_realloc(im->mi, im->num_alloc * sizeof(*im->mi));
188 im->mi[im->num_used].SizeOfStruct = sizeof(im->mi[im->num_used]);
189 if (SymGetModuleInfo64(dbg_curr_process->handle, base, &im->mi[im->num_used]))
191 im->num_used++;
193 return TRUE;
196 /***********************************************************************
197 * info_win32_module
199 * Display information about a given module (DLL or EXE), or about all modules
201 void info_win32_module(DWORD64 base)
203 struct info_module im;
204 UINT i, j, num_printed = 0;
205 DWORD opt;
207 if (!dbg_curr_process)
209 dbg_printf("Cannot get info on module while no process is loaded\n");
210 return;
213 im.mi = NULL;
214 im.num_alloc = im.num_used = 0;
216 /* this is a wine specific options to return also ELF modules in the
217 * enumeration
219 SymSetOptions((opt = SymGetOptions()) | 0x40000000);
220 SymEnumerateModules64(dbg_curr_process->handle, info_mod_cb, (void*)&im);
221 SymSetOptions(opt);
223 qsort(im.mi, im.num_used, sizeof(im.mi[0]), module_compare);
225 dbg_printf("Module\tAddress\t\t\tDebug info\tName (%d modules)\n", 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 (!GetClassName(hWnd, clsName, sizeof(clsName)))
284 return;
285 if ((atom = FindAtom(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)GetWindowLongPtr(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 (!GetClassInfoEx(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 (!GetClassName(hWnd, clsName, sizeof(clsName)))
370 strcpy(clsName, "-- Unknown --");
371 if (!GetWindowText(hWnd, wndName, sizeof(wndName)))
372 strcpy(wndName, "-- Empty --");
374 dbg_printf("%*s%08lx%*s %-17.17s %08x %08x %08x %.14s\n",
375 indent, "", (DWORD_PTR)hWnd, 12 - indent, "",
376 clsName, GetWindowLong(hWnd, GWL_STYLE),
377 GetWindowLongPtr(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 %-8.8s %-8.8s %s\n",
398 "Window handle", "Class Name", "Style", "WndProc", "Thread", "Text");
399 info_window(hWnd, 0);
400 return;
403 if (!GetClassName(hWnd, clsName, sizeof(clsName)))
404 strcpy(clsName, "-- Unknown --");
405 if (!GetWindowText(hWnd, wndName, sizeof(wndName)))
406 strcpy(wndName, "-- Empty --");
407 if (!GetClientRect(hWnd, &clientRect) ||
408 !MapWindowPoints(hWnd, 0, (LPPOINT) &clientRect, 2))
409 SetRectEmpty(&clientRect);
410 if (!GetWindowRect(hWnd, &windowRect))
411 SetRectEmpty(&windowRect);
413 /* FIXME missing fields: hmemTaskQ, hrgnUpdate, dce, flags, pProp, scroll */
414 dbg_printf("next=%p child=%p parent=%p owner=%p class='%s'\n"
415 "inst=%p active=%p idmenu=%08x\n"
416 "style=0x%08x exstyle=0x%08x wndproc=0x%08x text='%s'\n"
417 "client=%d,%d-%d,%d window=%d,%d-%d,%d sysmenu=%p\n",
418 GetWindow(hWnd, GW_HWNDNEXT),
419 GetWindow(hWnd, GW_CHILD),
420 GetParent(hWnd),
421 GetWindow(hWnd, GW_OWNER),
422 clsName,
423 (HINSTANCE)GetWindowLongPtr(hWnd, GWLP_HINSTANCE),
424 GetLastActivePopup(hWnd),
425 GetWindowLongPtr(hWnd, GWLP_ID),
426 GetWindowLong(hWnd, GWL_STYLE),
427 GetWindowLong(hWnd, GWL_EXSTYLE),
428 GetWindowLongPtr(hWnd, GWLP_WNDPROC),
429 wndName,
430 clientRect.left, clientRect.top, clientRect.right, clientRect.bottom,
431 windowRect.left, windowRect.top, windowRect.right, windowRect.bottom,
432 GetSystemMenu(hWnd, FALSE));
434 if (GetClassLong(hWnd, GCL_CBWNDEXTRA))
436 UINT i;
438 dbg_printf("Extra bytes:");
439 for (i = 0; i < GetClassLong(hWnd, GCL_CBWNDEXTRA) / 2; i++)
441 w = GetWindowWord(hWnd, i * 2);
442 /* FIXME: depends on i386 endian-ity */
443 dbg_printf(" %02x %02x", HIBYTE(w), LOBYTE(w));
445 dbg_printf("\n");
447 dbg_printf("\n");
450 void info_win32_processes(void)
452 HANDLE snap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
453 if (snap != INVALID_HANDLE_VALUE)
455 PROCESSENTRY32 entry;
456 DWORD current = dbg_curr_process ? dbg_curr_process->pid : 0;
457 BOOL ok;
459 entry.dwSize = sizeof(entry);
460 ok = Process32First(snap, &entry);
462 dbg_printf(" %-8.8s %-8.8s %-8.8s %s (all id:s are in hex)\n",
463 "pid", "threads", "parent", "executable");
464 while (ok)
466 if (entry.th32ProcessID != GetCurrentProcessId())
467 dbg_printf("%c%08x %-8d %08x '%s'\n",
468 (entry.th32ProcessID == current) ? '>' : ' ',
469 entry.th32ProcessID, entry.cntThreads,
470 entry.th32ParentProcessID, entry.szExeFile);
471 ok = Process32Next(snap, &entry);
473 CloseHandle(snap);
477 void info_win32_threads(void)
479 HANDLE snap = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
480 if (snap != INVALID_HANDLE_VALUE)
482 THREADENTRY32 entry;
483 BOOL ok;
484 DWORD lastProcessId = 0;
486 entry.dwSize = sizeof(entry);
487 ok = Thread32First(snap, &entry);
489 dbg_printf("%-8.8s %-8.8s %s (all id:s are in hex)\n",
490 "process", "tid", "prio");
491 while (ok)
493 if (entry.th32OwnerProcessID != GetCurrentProcessId())
495 /* FIXME: this assumes that, in the snapshot, all threads of a same process are
496 * listed sequentially, which is not specified in the doc (Wine's implementation
497 * does it)
499 if (entry.th32OwnerProcessID != lastProcessId)
501 struct dbg_process* p = dbg_get_process(entry.th32OwnerProcessID);
503 dbg_printf("%08x%s %s\n",
504 entry.th32OwnerProcessID, p ? " (D)" : "",
505 p ? dbg_W2A(p->imageName, -1) : "");
506 lastProcessId = entry.th32OwnerProcessID;
508 dbg_printf("\t%08x %4d%s\n",
509 entry.th32ThreadID, entry.tpBasePri,
510 (entry.th32ThreadID == dbg_curr_tid) ? " <==" : "");
513 ok = Thread32Next(snap, &entry);
516 CloseHandle(snap);
520 /***********************************************************************
521 * info_win32_exceptions
523 * Get info on the exception frames of a given thread.
525 void info_win32_exceptions(DWORD tid)
527 struct dbg_thread* thread;
528 void* next_frame;
530 if (!dbg_curr_process || !dbg_curr_thread)
532 dbg_printf("Cannot get info on exceptions while no process is loaded\n");
533 return;
536 dbg_printf("Exception frames:\n");
538 if (tid == dbg_curr_tid) thread = dbg_curr_thread;
539 else
541 thread = dbg_get_thread(dbg_curr_process, tid);
543 if (!thread)
545 dbg_printf("Unknown thread id (%04x) in current process\n", tid);
546 return;
548 if (SuspendThread(thread->handle) == -1)
550 dbg_printf("Can't suspend thread id (%04x)\n", tid);
551 return;
555 if (!dbg_read_memory(thread->teb, &next_frame, sizeof(next_frame)))
557 dbg_printf("Can't read TEB:except_frame\n");
558 return;
561 while (next_frame != (void*)-1)
563 EXCEPTION_REGISTRATION_RECORD frame;
565 dbg_printf("%p: ", next_frame);
566 if (!dbg_read_memory(next_frame, &frame, sizeof(frame)))
568 dbg_printf("Invalid frame address\n");
569 break;
571 dbg_printf("prev=%p handler=%p\n", frame.Prev, frame.Handler);
572 next_frame = frame.Prev;
575 if (tid != dbg_curr_tid) ResumeThread(thread->handle);
578 void info_win32_segments(DWORD start, int length)
580 char flags[3];
581 DWORD i;
582 LDT_ENTRY le;
584 if (length == -1) length = (8192 - start);
586 for (i = start; i < start + length; i++)
588 if (!dbg_curr_process->process_io->get_selector(dbg_curr_thread->handle, (i << 3) | 7, &le))
589 continue;
591 if (le.HighWord.Bits.Type & 0x08)
593 flags[0] = (le.HighWord.Bits.Type & 0x2) ? 'r' : '-';
594 flags[1] = '-';
595 flags[2] = 'x';
597 else
599 flags[0] = 'r';
600 flags[1] = (le.HighWord.Bits.Type & 0x2) ? 'w' : '-';
601 flags[2] = '-';
603 dbg_printf("%04x: sel=%04x base=%08x limit=%08x %d-bit %c%c%c\n",
604 i, (i << 3) | 7,
605 (le.HighWord.Bits.BaseHi << 24) +
606 (le.HighWord.Bits.BaseMid << 16) + le.BaseLow,
607 ((le.HighWord.Bits.LimitHi << 8) + le.LimitLow) <<
608 (le.HighWord.Bits.Granularity ? 12 : 0),
609 le.HighWord.Bits.Default_Big ? 32 : 16,
610 flags[0], flags[1], flags[2]);
614 void info_win32_virtual(DWORD pid)
616 MEMORY_BASIC_INFORMATION mbi;
617 char* addr = 0;
618 const char* state;
619 const char* type;
620 char prot[3+1];
621 HANDLE hProc;
623 if (pid == dbg_curr_pid)
625 if (dbg_curr_process == NULL)
627 dbg_printf("Cannot look at mapping of current process, while no process is loaded\n");
628 return;
630 hProc = dbg_curr_process->handle;
632 else
634 hProc = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, pid);
635 if (hProc == NULL)
637 dbg_printf("Cannot open process <%04x>\n", pid);
638 return;
642 dbg_printf("Address Size State Type RWX\n");
644 while (VirtualQueryEx(hProc, addr, &mbi, sizeof(mbi)) >= sizeof(mbi))
646 switch (mbi.State)
648 case MEM_COMMIT: state = "commit "; break;
649 case MEM_FREE: state = "free "; break;
650 case MEM_RESERVE: state = "reserve"; break;
651 default: state = "??? "; break;
653 if (mbi.State != MEM_FREE)
655 switch (mbi.Type)
657 case MEM_IMAGE: type = "image "; break;
658 case MEM_MAPPED: type = "mapped "; break;
659 case MEM_PRIVATE: type = "private"; break;
660 case 0: type = " "; break;
661 default: type = "??? "; break;
663 memset(prot, ' ' , sizeof(prot) - 1);
664 prot[sizeof(prot) - 1] = '\0';
665 if (mbi.AllocationProtect & (PAGE_READONLY|PAGE_READWRITE|PAGE_EXECUTE_READ|PAGE_EXECUTE_READWRITE))
666 prot[0] = 'R';
667 if (mbi.AllocationProtect & (PAGE_READWRITE|PAGE_EXECUTE_READWRITE))
668 prot[1] = 'W';
669 if (mbi.AllocationProtect & (PAGE_WRITECOPY|PAGE_EXECUTE_WRITECOPY))
670 prot[1] = 'C';
671 if (mbi.AllocationProtect & (PAGE_EXECUTE|PAGE_EXECUTE_READ|PAGE_EXECUTE_READWRITE))
672 prot[2] = 'X';
674 else
676 type = "";
677 prot[0] = '\0';
679 dbg_printf("%08lx %08lx %s %s %s\n",
680 (DWORD_PTR)addr, (DWORD_PTR)addr + mbi.RegionSize - 1, state, type, prot);
681 if (addr + mbi.RegionSize < addr) /* wrap around ? */
682 break;
683 addr += mbi.RegionSize;
685 if (pid != dbg_curr_pid) CloseHandle(hProc);
688 void info_wine_dbg_channel(BOOL turn_on, const char* cls, const char* name)
690 struct dbg_lvalue lvalue;
691 struct __wine_debug_channel channel;
692 unsigned char mask;
693 int done = 0;
694 BOOL bAll;
695 void* addr;
697 if (!dbg_curr_process || !dbg_curr_thread)
699 dbg_printf("Cannot set/get debug channels while no process is loaded\n");
700 return;
703 if (symbol_get_lvalue("debug_options", -1, &lvalue, FALSE) != sglv_found)
705 return;
707 addr = memory_to_linear_addr(&lvalue.addr);
709 if (!cls) mask = ~0;
710 else if (!strcmp(cls, "fixme")) mask = (1 << __WINE_DBCL_FIXME);
711 else if (!strcmp(cls, "err")) mask = (1 << __WINE_DBCL_ERR);
712 else if (!strcmp(cls, "warn")) mask = (1 << __WINE_DBCL_WARN);
713 else if (!strcmp(cls, "trace")) mask = (1 << __WINE_DBCL_TRACE);
714 else
716 dbg_printf("Unknown debug class %s\n", cls);
717 return;
720 bAll = !strcmp("all", name);
721 while (addr && dbg_read_memory(addr, &channel, sizeof(channel)))
723 if (!channel.name[0]) break;
724 if (bAll || !strcmp( channel.name, name ))
726 if (turn_on) channel.flags |= mask;
727 else channel.flags &= ~mask;
728 if (dbg_write_memory(addr, &channel, sizeof(channel))) done++;
730 addr = (struct __wine_debug_channel *)addr + 1;
732 if (!done) dbg_printf("Unable to find debug channel %s\n", name);
733 else WINE_TRACE("Changed %d channel instances\n", done);