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
28 * - the dialog box could be non modal
30 * + could refresh channels from time to time
31 * - get a better UI (replace the 'x' by real tick boxes in list view)
32 * - implement a real solution around the get_symbol hack
33 * - enhance visual feedback: the list is large, and it's hard to get the
34 * right line when clicking on rightmost column (trace for example)
35 * - get rid of printfs (error reporting) and use real message boxes
36 * - include the column width settings in the full column management scheme
39 BOOL
DebugChannelsAreSupported(void)
47 static int list_channel_CB(HANDLE hProcess
, void* addr
, WCHAR
* buffer
, void* user
)
53 HWND hChannelLV
= (HWND
)user
;
55 memset(&lvi
, 0, sizeof(lvi
));
58 lvi
.pszText
= buffer
+ 1;
60 index
= ListView_InsertItem(hChannelLV
, &lvi
);
61 if (index
== -1) return 0;
64 for (j
= 0; j
< 4; j
++)
66 val
[0] = (buffer
[0] & (1 << j
)) ? L
'x' : L
' ';
67 ListView_SetItemText(hChannelLV
, index
, j
+ 1, val
);
74 LPCWSTR name
; /* channel to look for */
75 unsigned value
, mask
; /* how to change channel */
76 unsigned done
; /* number of successful changes */
77 unsigned notdone
; /* number of unsuccessful changes */
80 /******************************************************************
83 * Callback used for changing a given channel attributes
85 static int change_channel_CB(HANDLE hProcess
, void* addr
, WCHAR
* buffer
, void* pmt
)
87 struct cce_user
* user
= (struct cce_user
*)pmt
;
89 if (!user
->name
|| !wcscmp(buffer
+ 1, user
->name
))
91 buffer
[0] = (buffer
[0] & ~user
->mask
) | (user
->value
& user
->mask
);
92 if (WriteProcessMemory(hProcess
, addr
, buffer
, 1, NULL
))
101 /******************************************************************
104 * Here it gets ugly :-(
105 * This is quick hack to get the address of first_dll in a running process
106 * We make the following assumptions:
107 * - libwine (lib) is loaded in all processes at the same address (or
108 * at least at the same address at this process)
109 * - we load the same libwine.so version in this process and in the
111 * Final address is gotten by: 1/ querying the address of a known exported
112 * symbol out of libwine.so with dlsym, 2/ then querying nm on libwine.so to
113 * get the offset from the data segment of this known symbol and of first_dll,
114 * 3/ computing the actual address of first_dll by adding the result of 1/ and
116 * Ugly, yes, but it somehow works. We should replace that with debughlp
117 * library, that'd be way better. Exporting first_dll from libwine.so would make
118 * this code simpler, but still ugly.
120 /* FIXME: we only need those includes for the next function */
121 #include <dlfcn.h> /* for RTLD_LAZY */
122 #include <sys/types.h>
123 #include <sys/stat.h>
126 #include "wine/library.h"
128 void* get_symbol(HANDLE hProcess
, const char* name
, const char* lib
)
132 DWORD addr
= 0, tmp
= 0;
136 if (!(h
= wine_dlopen(lib
, RTLD_LAZY
, buffer
, sizeof(buffer
))))
138 printf("Couldn't load %s (%s)\n", lib
, buffer
);
142 env
= getenv("LD_LIBRARY_PATH");
148 for (ptr
= env
= strdup(env
); ptr
; ptr
= next
)
150 next
= strchr(ptr
, ':');
151 if (next
) *next
++ = '\0';
152 sprintf(buffer
, "nm %s", ptr
);
153 if (buffer
[strlen(buffer
) - 1] != '/') strcat(buffer
, "/");
155 if (stat(buffer
+ 3, &s
) == 0) break;
160 printf("Couldn't find %s in LD_LIBRARY_PATH\n", lib
);
164 if (!(f
= popen(buffer
, "r")))
166 printf("Cannot execute '%s'\n", buffer
);
170 while (fgets(buffer
, sizeof(buffer
), f
))
172 char *p
= buffer
+ strlen(buffer
) - 1;
173 if (p
< buffer
) continue;
174 if (*p
== '\n') *p
-- = 0;
175 if (p
- buffer
< 11) continue;
177 if (!strcmp(&buffer
[11], name
)) addr
+= strtol(buffer
, NULL
, 16);
178 if (buffer
[9] == 'D' && !tmp
&& (tmp
= (DWORD
)wine_dlsym(h
, &buffer
[11], NULL
, 0)) != 0)
179 addr
+= tmp
- strtol(buffer
, NULL
, 16);
185 void* get_symbol(HANDLE hProcess
, const char* name
, const char* lib
)
187 printf("get_symbol: not implemented on this platform\n");
192 struct dll_option_layout
196 char* const* channels
;
197 unsigned int nb_channels
;
200 typedef int (*EnumChannelCB
)(HANDLE
, void*, WCHAR
*, void*);
202 /******************************************************************
205 * Enumerates all known channels on process hProcess through callback
208 static int enum_channel(HANDLE hProcess
, EnumChannelCB ce
, void* user
, unsigned unique
)
210 struct dll_option_layout dol
;
215 WCHAR
** cache
= NULL
;
216 unsigned i
, j
, num_cache
, used_cache
;
218 addr
= get_symbol(hProcess
, "first_dll", "libwine.so");
219 if (!addr
) return -1;
221 cache
= HeapAlloc(GetProcessHeap(), 0, (num_cache
= 32) * sizeof(WCHAR
*));
227 ret
&& addr
&& ReadProcessMemory(hProcess
, addr
, &dol
, sizeof(dol
), NULL
);
230 for (i
= 0; i
< dol
.nb_channels
; i
++)
232 if (ReadProcessMemory(hProcess
, (void*)(dol
.channels
+ i
), &buf_addr
, sizeof(buf_addr
), NULL
) &&
233 ReadProcessMemory(hProcess
, buf_addr
, buffer
, sizeof(buffer
), NULL
))
237 /* since some channels are defined in multiple compilation units,
238 * they will appear several times...
239 * so cache the channel's names we already reported and don't report
242 for (j
= 0; j
< used_cache
; j
++)
243 if (!wcscmp(cache
[j
], buffer
+ 1)) break;
244 if (j
!= used_cache
) continue;
245 if (used_cache
== num_cache
)
246 cache
= HeapReAlloc(GetProcessHeap(), 0, cache
, (num_cache
*= 2) * sizeof(WCHAR
*));
247 cache
[used_cache
++] = wcscpy(HeapAlloc(GetProcessHeap(), 0, (wcslen(buffer
+ 1) + 1) * sizeof(WCHAR
)),
250 ret
= ce(hProcess
, buf_addr
, buffer
, user
);
256 for (j
= 0; j
< used_cache
; j
++) HeapFree(GetProcessHeap(), 0, (WCHAR
*)cache
[j
]);
257 HeapFree(GetProcessHeap(), 0, cache
);
262 static void DebugChannels_FillList(HWND hChannelLV
)
266 (void)ListView_DeleteAllItems(hChannelLV
);
268 hProcess
= OpenProcess(PROCESS_VM_OPERATION
| PROCESS_VM_READ
, FALSE
, GetSelectedProcessId());
269 if (!hProcess
) return; /* FIXME messagebox */
270 SendMessageW(hChannelLV
, WM_SETREDRAW
, FALSE
, 0);
271 enum_channel(hProcess
, list_channel_CB
, (void*)hChannelLV
, TRUE
);
272 SendMessageW(hChannelLV
, WM_SETREDRAW
, TRUE
, 0);
273 CloseHandle(hProcess
);
276 static void DebugChannels_OnCreate(HWND hwndDlg
)
278 HWND hLV
= GetDlgItem(hwndDlg
, IDC_DEBUG_CHANNELS_LIST
);
281 lvc
.mask
= LVCF_FMT
| LVCF_TEXT
| LVCF_WIDTH
;
282 lvc
.fmt
= LVCFMT_LEFT
;
283 lvc
.pszText
= L
"Debug Channel";
285 (void)ListView_InsertColumn(hLV
, 0, &lvc
);
287 lvc
.mask
= LVCF_FMT
| LVCF_TEXT
| LVCF_WIDTH
;
288 lvc
.fmt
= LVCFMT_CENTER
;
289 lvc
.pszText
= L
"Fixme";
291 (void)ListView_InsertColumn(hLV
, 1, &lvc
);
293 lvc
.mask
= LVCF_FMT
| LVCF_TEXT
| LVCF_WIDTH
;
294 lvc
.fmt
= LVCFMT_CENTER
;
295 lvc
.pszText
= L
"Err";
297 (void)ListView_InsertColumn(hLV
, 2, &lvc
);
299 lvc
.mask
= LVCF_FMT
| LVCF_TEXT
| LVCF_WIDTH
;
300 lvc
.fmt
= LVCFMT_CENTER
;
301 lvc
.pszText
= L
"Warn";
303 (void)ListView_InsertColumn(hLV
, 3, &lvc
);
305 lvc
.mask
= LVCF_FMT
| LVCF_TEXT
| LVCF_WIDTH
;
306 lvc
.fmt
= LVCFMT_CENTER
;
307 lvc
.pszText
= L
"Trace";
309 (void)ListView_InsertColumn(hLV
, 4, &lvc
);
311 DebugChannels_FillList(hLV
);
314 static void DebugChannels_OnNotify(HWND hDlg
, LPARAM lParam
)
316 NMHDR
* nmh
= (NMHDR
*)lParam
;
321 if (nmh
->idFrom
== IDC_DEBUG_CHANNELS_LIST
)
326 NMITEMACTIVATE
* nmia
= (NMITEMACTIVATE
*)lParam
;
328 hProcess
= OpenProcess(PROCESS_VM_OPERATION
| PROCESS_VM_READ
| PROCESS_VM_WRITE
, FALSE
, GetSelectedProcessId());
329 if (!hProcess
) return; /* FIXME message box */
330 lhti
.pt
= nmia
->ptAction
;
331 hChannelLV
= GetDlgItem(hDlg
, IDC_DEBUG_CHANNELS_LIST
);
332 SendMessageW(hChannelLV
, LVM_SUBITEMHITTEST
, 0, (LPARAM
)&lhti
);
333 if (nmia
->iSubItem
>= 1 && nmia
->iSubItem
<= 4)
337 unsigned bitmask
= 1 << (lhti
.iSubItem
- 1);
338 struct cce_user user
;
340 ListView_GetItemText(hChannelLV
, lhti
.iItem
, 0, name
, sizeof(name
) / sizeof(name
[0]));
341 ListView_GetItemText(hChannelLV
, lhti
.iItem
, lhti
.iSubItem
, val
, sizeof(val
) / sizeof(val
[0]));
343 user
.value
= (val
[0] == L
'x') ? 0 : bitmask
;
345 user
.done
= user
.notdone
= 0;
346 enum_channel(hProcess
, change_channel_CB
, &user
, FALSE
);
349 val
[0] ^= (L
'x' ^ L
' ');
350 ListView_SetItemText(hChannelLV
, lhti
.iItem
, lhti
.iSubItem
, val
);
353 printf("Some channel instance weren't correctly set\n");
355 CloseHandle(hProcess
);
361 static INT_PTR CALLBACK
DebugChannelsDlgProc(HWND hDlg
, UINT message
, WPARAM wParam
, LPARAM lParam
)
366 DebugChannels_OnCreate(hDlg
);
369 if (LOWORD(wParam
) == IDOK
|| LOWORD(wParam
) == IDCANCEL
) {
370 EndDialog(hDlg
, LOWORD(wParam
));
375 DebugChannels_OnNotify(hDlg
, lParam
);
381 void ProcessPage_OnDebugChannels(void)
383 DialogBoxW(hInst
, (LPCWSTR
)IDD_DEBUG_CHANNELS_DIALOG
, hMainWnd
, DebugChannelsDlgProc
);