6 * Copyright (C) 2003 - 2004 Eric Pouech
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2.1 of the License, or (at your option) any later version.
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
26 * - the dialog box could be non modal
28 * + could refresh channels from time to time
29 * - get a better UI (replace the 'x' by real tick boxes in list view)
30 * - implement a real solution around the get_symbol hack
31 * - enhance visual feedback: the list is large, and it's hard to get the
32 * right line when clicking on rightmost column (trace for example)
33 * - get rid of printfs (error reporting) and use real message boxes
34 * - include the column width settings in the full column management scheme
37 BOOL
DebugChannelsAreSupported(void)
45 static int list_channel_CB(HANDLE hProcess
, void* addr
, WCHAR
* buffer
, void* user
)
51 HWND hChannelLV
= (HWND
)user
;
53 memset(&lvi
, 0, sizeof(lvi
));
56 lvi
.pszText
= buffer
+ 1;
58 index
= ListView_InsertItem(hChannelLV
, &lvi
);
59 if (index
== -1) return 0;
62 for (j
= 0; j
< 4; j
++)
64 val
[0] = (buffer
[0] & (1 << j
)) ? L
'x' : L
' ';
65 ListView_SetItemText(hChannelLV
, index
, j
+ 1, val
);
72 LPCWSTR name
; /* channel to look for */
73 unsigned value
, mask
; /* how to change channel */
74 unsigned done
; /* number of successful changes */
75 unsigned notdone
; /* number of unsuccessful changes */
78 /******************************************************************
81 * Callback used for changing a given channel attributes
83 static int change_channel_CB(HANDLE hProcess
, void* addr
, WCHAR
* buffer
, void* pmt
)
85 struct cce_user
* user
= (struct cce_user
*)pmt
;
87 if (!user
->name
|| !wcscmp(buffer
+ 1, user
->name
))
89 buffer
[0] = (buffer
[0] & ~user
->mask
) | (user
->value
& user
->mask
);
90 if (WriteProcessMemory(hProcess
, addr
, buffer
, 1, NULL
))
99 /******************************************************************
102 * Here it gets ugly :-(
103 * This is quick hack to get the address of first_dll in a running process
104 * We make the following assumptions:
105 * - libwine (lib) is loaded in all processes at the same address (or
106 * at least at the same address at this process)
107 * - we load the same libwine.so version in this process and in the
109 * Final address is gotten by: 1/ querying the address of a known exported
110 * symbol out of libwine.so with dlsym, 2/ then querying nm on libwine.so to
111 * get the offset from the data segment of this known symbol and of first_dll,
112 * 3/ computing the actual address of first_dll by adding the result of 1/ and
114 * Ugly, yes, but it somehow works. We should replace that with debughlp
115 * library, that'd be way better. Exporting first_dll from libwine.so would make
116 * this code simpler, but still ugly.
118 /* FIXME: we only need those includes for the next function */
119 #include <dlfcn.h> /* for RTLD_LAZY */
120 #include <sys/types.h>
121 #include <sys/stat.h>
124 #include "wine/library.h"
126 void* get_symbol(HANDLE hProcess
, const char* name
, const char* lib
)
130 DWORD addr
= 0, tmp
= 0;
134 if (!(h
= wine_dlopen(lib
, RTLD_LAZY
, buffer
, sizeof(buffer
))))
136 printf("Couldn't load %s (%s)\n", lib
, buffer
);
140 env
= getenv("LD_LIBRARY_PATH");
146 for (ptr
= env
= strdup(env
); ptr
; ptr
= next
)
148 next
= strchr(ptr
, ':');
149 if (next
) *next
++ = '\0';
150 sprintf(buffer
, "nm %s", ptr
);
151 if (buffer
[strlen(buffer
) - 1] != '/') strcat(buffer
, "/");
153 if (stat(buffer
+ 3, &s
) == 0) break;
158 printf("Couldn't find %s in LD_LIBRARY_PATH\n", lib
);
162 if (!(f
= popen(buffer
, "r")))
164 printf("Cannot execute '%s'\n", buffer
);
168 while (fgets(buffer
, sizeof(buffer
), f
))
170 char *p
= buffer
+ strlen(buffer
) - 1;
171 if (p
< buffer
) continue;
172 if (*p
== '\n') *p
-- = 0;
173 if (p
- buffer
< 11) continue;
175 if (!strcmp(&buffer
[11], name
)) addr
+= strtol(buffer
, NULL
, 16);
176 if (buffer
[9] == 'D' && !tmp
&& (tmp
= (DWORD
)wine_dlsym(h
, &buffer
[11], NULL
, 0)) != 0)
177 addr
+= tmp
- strtol(buffer
, NULL
, 16);
183 void* get_symbol(HANDLE hProcess
, const char* name
, const char* lib
)
185 printf("get_symbol: not implemented on this platform\n");
190 struct dll_option_layout
194 char* const* channels
;
195 unsigned int nb_channels
;
198 typedef int (*EnumChannelCB
)(HANDLE
, void*, WCHAR
*, void*);
200 /******************************************************************
203 * Enumerates all known channels on process hProcess through callback
206 static int enum_channel(HANDLE hProcess
, EnumChannelCB ce
, void* user
, unsigned unique
)
208 struct dll_option_layout dol
;
213 WCHAR
** cache
= NULL
;
214 unsigned i
, j
, num_cache
, used_cache
;
216 addr
= get_symbol(hProcess
, "first_dll", "libwine.so");
217 if (!addr
) return -1;
219 cache
= HeapAlloc(GetProcessHeap(), 0, (num_cache
= 32) * sizeof(WCHAR
*));
225 ret
&& addr
&& ReadProcessMemory(hProcess
, addr
, &dol
, sizeof(dol
), NULL
);
228 for (i
= 0; i
< dol
.nb_channels
; i
++)
230 if (ReadProcessMemory(hProcess
, (void*)(dol
.channels
+ i
), &buf_addr
, sizeof(buf_addr
), NULL
) &&
231 ReadProcessMemory(hProcess
, buf_addr
, buffer
, sizeof(buffer
), NULL
))
235 /* since some channels are defined in multiple compilation units,
236 * they will appear several times...
237 * so cache the channel's names we already reported and don't report
240 for (j
= 0; j
< used_cache
; j
++)
241 if (!wcscmp(cache
[j
], buffer
+ 1)) break;
242 if (j
!= used_cache
) continue;
243 if (used_cache
== num_cache
)
244 cache
= HeapReAlloc(GetProcessHeap(), 0, cache
, (num_cache
*= 2) * sizeof(WCHAR
*));
245 cache
[used_cache
++] = wcscpy(HeapAlloc(GetProcessHeap(), 0, (wcslen(buffer
+ 1) + 1) * sizeof(WCHAR
)),
248 ret
= ce(hProcess
, buf_addr
, buffer
, user
);
254 for (j
= 0; j
< used_cache
; j
++) HeapFree(GetProcessHeap(), 0, (WCHAR
*)cache
[j
]);
255 HeapFree(GetProcessHeap(), 0, cache
);
260 static void DebugChannels_FillList(HWND hChannelLV
)
264 (void)ListView_DeleteAllItems(hChannelLV
);
266 hProcess
= OpenProcess(PROCESS_VM_OPERATION
| PROCESS_VM_READ
, FALSE
, GetSelectedProcessId());
267 if (!hProcess
) return; /* FIXME messagebox */
268 SendMessageW(hChannelLV
, WM_SETREDRAW
, FALSE
, 0);
269 enum_channel(hProcess
, list_channel_CB
, (void*)hChannelLV
, TRUE
);
270 SendMessageW(hChannelLV
, WM_SETREDRAW
, TRUE
, 0);
271 CloseHandle(hProcess
);
274 static void DebugChannels_OnCreate(HWND hwndDlg
)
276 HWND hLV
= GetDlgItem(hwndDlg
, IDC_DEBUG_CHANNELS_LIST
);
279 lvc
.mask
= LVCF_FMT
| LVCF_TEXT
| LVCF_WIDTH
;
280 lvc
.fmt
= LVCFMT_LEFT
;
281 lvc
.pszText
= L
"Debug Channel";
283 (void)ListView_InsertColumn(hLV
, 0, &lvc
);
285 lvc
.mask
= LVCF_FMT
| LVCF_TEXT
| LVCF_WIDTH
;
286 lvc
.fmt
= LVCFMT_CENTER
;
287 lvc
.pszText
= L
"Fixme";
289 (void)ListView_InsertColumn(hLV
, 1, &lvc
);
291 lvc
.mask
= LVCF_FMT
| LVCF_TEXT
| LVCF_WIDTH
;
292 lvc
.fmt
= LVCFMT_CENTER
;
293 lvc
.pszText
= L
"Err";
295 (void)ListView_InsertColumn(hLV
, 2, &lvc
);
297 lvc
.mask
= LVCF_FMT
| LVCF_TEXT
| LVCF_WIDTH
;
298 lvc
.fmt
= LVCFMT_CENTER
;
299 lvc
.pszText
= L
"Warn";
301 (void)ListView_InsertColumn(hLV
, 3, &lvc
);
303 lvc
.mask
= LVCF_FMT
| LVCF_TEXT
| LVCF_WIDTH
;
304 lvc
.fmt
= LVCFMT_CENTER
;
305 lvc
.pszText
= L
"Trace";
307 (void)ListView_InsertColumn(hLV
, 4, &lvc
);
309 DebugChannels_FillList(hLV
);
312 static void DebugChannels_OnNotify(HWND hDlg
, LPARAM lParam
)
314 NMHDR
* nmh
= (NMHDR
*)lParam
;
319 if (nmh
->idFrom
== IDC_DEBUG_CHANNELS_LIST
)
324 NMITEMACTIVATE
* nmia
= (NMITEMACTIVATE
*)lParam
;
326 hProcess
= OpenProcess(PROCESS_VM_OPERATION
| PROCESS_VM_READ
| PROCESS_VM_WRITE
, FALSE
, GetSelectedProcessId());
327 if (!hProcess
) return; /* FIXME message box */
328 lhti
.pt
= nmia
->ptAction
;
329 hChannelLV
= GetDlgItem(hDlg
, IDC_DEBUG_CHANNELS_LIST
);
330 SendMessageW(hChannelLV
, LVM_SUBITEMHITTEST
, 0, (LPARAM
)&lhti
);
331 if (nmia
->iSubItem
>= 1 && nmia
->iSubItem
<= 4)
335 unsigned bitmask
= 1 << (lhti
.iSubItem
- 1);
336 struct cce_user user
;
338 ListView_GetItemText(hChannelLV
, lhti
.iItem
, 0, name
, sizeof(name
) / sizeof(name
[0]));
339 ListView_GetItemText(hChannelLV
, lhti
.iItem
, lhti
.iSubItem
, val
, sizeof(val
) / sizeof(val
[0]));
341 user
.value
= (val
[0] == L
'x') ? 0 : bitmask
;
343 user
.done
= user
.notdone
= 0;
344 enum_channel(hProcess
, change_channel_CB
, &user
, FALSE
);
347 val
[0] ^= (L
'x' ^ L
' ');
348 ListView_SetItemText(hChannelLV
, lhti
.iItem
, lhti
.iSubItem
, val
);
351 printf("Some channel instance weren't correctly set\n");
353 CloseHandle(hProcess
);
359 static INT_PTR CALLBACK
DebugChannelsDlgProc(HWND hDlg
, UINT message
, WPARAM wParam
, LPARAM lParam
)
364 DebugChannels_OnCreate(hDlg
);
367 if (LOWORD(wParam
) == IDOK
|| LOWORD(wParam
) == IDCANCEL
) {
368 EndDialog(hDlg
, LOWORD(wParam
));
373 DebugChannels_OnNotify(hDlg
, lParam
);
379 void ProcessPage_OnDebugChannels(void)
381 DialogBoxW(hInst
, (LPCWSTR
)IDD_DEBUG_CHANNELS_DIALOG
, hMainWnd
, DebugChannelsDlgProc
);