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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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 DWORD
get_selected_pid(void)
51 for (Index
=0; Index
<(ULONG
)ListView_GetItemCount(hProcessPageListCtrl
); Index
++)
53 memset(&lvitem
, 0, sizeof(LVITEM
));
55 lvitem
.mask
= LVIF_STATE
;
56 lvitem
.stateMask
= LVIS_SELECTED
;
59 (void)ListView_GetItem(hProcessPageListCtrl
, &lvitem
);
61 if (lvitem
.state
& LVIS_SELECTED
)
65 dwProcessId
= PerfDataGetProcessId(Index
);
67 if ((ListView_GetSelectedCount(hProcessPageListCtrl
) != 1) || (dwProcessId
== 0))
72 static int list_channel_CB(HANDLE hProcess
, void* addr
, WCHAR
* buffer
, void* user
)
78 HWND hChannelLV
= (HWND
)user
;
80 memset(&lvi
, 0, sizeof(lvi
));
83 lvi
.pszText
= buffer
+ 1;
85 index
= ListView_InsertItem(hChannelLV
, &lvi
);
86 if (index
== -1) return 0;
89 for (j
= 0; j
< 4; j
++)
91 val
[0] = (buffer
[0] & (1 << j
)) ? L
'x' : L
' ';
92 ListView_SetItemText(hChannelLV
, index
, j
+ 1, val
);
99 LPCWSTR name
; /* channel to look for */
100 unsigned value
, mask
; /* how to change channel */
101 unsigned done
; /* number of successful changes */
102 unsigned notdone
; /* number of unsuccessful changes */
105 /******************************************************************
108 * Callback used for changing a given channel attributes
110 static int change_channel_CB(HANDLE hProcess
, void* addr
, WCHAR
* buffer
, void* pmt
)
112 struct cce_user
* user
= (struct cce_user
*)pmt
;
114 if (!user
->name
|| !wcscmp(buffer
+ 1, user
->name
))
116 buffer
[0] = (buffer
[0] & ~user
->mask
) | (user
->value
& user
->mask
);
117 if (WriteProcessMemory(hProcess
, addr
, buffer
, 1, NULL
))
126 /******************************************************************
129 * Here it gets ugly :-(
130 * This is quick hack to get the address of first_dll in a running process
131 * We make the following assumptions:
132 * - libwine (lib) is loaded in all processes at the same address (or
133 * at least at the same address at this process)
134 * - we load the same libwine.so version in this process and in the
136 * Final address is gotten by: 1/ querying the address of a known exported
137 * symbol out of libwine.so with dlsym, 2/ then querying nm on libwine.so to
138 * get the offset from the data segment of this known symbol and of first_dll,
139 * 3/ computing the actual address of first_dll by adding the result of 1/ and
141 * Ugly, yes, but it somehow works. We should replace that with debughlp
142 * library, that'd be way better. Exporting first_dll from libwine.so would make
143 * this code simpler, but still ugly.
145 /* FIXME: we only need those includes for the next function */
146 #include <dlfcn.h> /* for RTLD_LAZY */
147 #include <sys/types.h>
148 #include <sys/stat.h>
151 #include "wine/library.h"
153 void* get_symbol(HANDLE hProcess
, const char* name
, const char* lib
)
157 DWORD addr
= 0, tmp
= 0;
161 if (!(h
= wine_dlopen(lib
, RTLD_LAZY
, buffer
, sizeof(buffer
))))
163 printf("Couldn't load %s (%s)\n", lib
, buffer
);
167 env
= getenv("LD_LIBRARY_PATH");
173 for (ptr
= env
= strdup(env
); ptr
; ptr
= next
)
175 next
= strchr(ptr
, ':');
176 if (next
) *next
++ = '\0';
177 sprintf(buffer
, "nm %s", ptr
);
178 if (buffer
[strlen(buffer
) - 1] != '/') strcat(buffer
, "/");
180 if (stat(buffer
+ 3, &s
) == 0) break;
185 printf("Couldn't find %s in LD_LIBRARY_PATH\n", lib
);
189 if (!(f
= popen(buffer
, "r")))
191 printf("Cannot execute '%s'\n", buffer
);
195 while (fgets(buffer
, sizeof(buffer
), f
))
197 char *p
= buffer
+ strlen(buffer
) - 1;
198 if (p
< buffer
) continue;
199 if (*p
== '\n') *p
-- = 0;
200 if (p
- buffer
< 11) continue;
202 if (!strcmp(&buffer
[11], name
)) addr
+= strtol(buffer
, NULL
, 16);
203 if (buffer
[9] == 'D' && !tmp
&& (tmp
= (DWORD
)wine_dlsym(h
, &buffer
[11], NULL
, 0)) != 0)
204 addr
+= tmp
- strtol(buffer
, NULL
, 16);
210 void* get_symbol(HANDLE hProcess
, const char* name
, const char* lib
)
212 printf("get_symbol: not implemented on this platform\n");
217 struct dll_option_layout
221 char* const* channels
;
222 unsigned int nb_channels
;
225 typedef int (*EnumChannelCB
)(HANDLE
, void*, WCHAR
*, void*);
227 /******************************************************************
230 * Enumerates all known channels on process hProcess through callback
233 static int enum_channel(HANDLE hProcess
, EnumChannelCB ce
, void* user
, unsigned unique
)
235 struct dll_option_layout dol
;
240 WCHAR
** cache
= NULL
;
241 unsigned i
, j
, num_cache
, used_cache
;
243 addr
= get_symbol(hProcess
, "first_dll", "libwine.so");
244 if (!addr
) return -1;
246 cache
= HeapAlloc(GetProcessHeap(), 0, (num_cache
= 32) * sizeof(WCHAR
*));
252 ret
&& addr
&& ReadProcessMemory(hProcess
, addr
, &dol
, sizeof(dol
), NULL
);
255 for (i
= 0; i
< dol
.nb_channels
; i
++)
257 if (ReadProcessMemory(hProcess
, (void*)(dol
.channels
+ i
), &buf_addr
, sizeof(buf_addr
), NULL
) &&
258 ReadProcessMemory(hProcess
, buf_addr
, buffer
, sizeof(buffer
), NULL
))
262 /* since some channels are defined in multiple compilation units,
263 * they will appear several times...
264 * so cache the channel's names we already reported and don't report
267 for (j
= 0; j
< used_cache
; j
++)
268 if (!wcscmp(cache
[j
], buffer
+ 1)) break;
269 if (j
!= used_cache
) continue;
270 if (used_cache
== num_cache
)
271 cache
= HeapReAlloc(GetProcessHeap(), 0, cache
, (num_cache
*= 2) * sizeof(WCHAR
*));
272 cache
[used_cache
++] = wcscpy(HeapAlloc(GetProcessHeap(), 0, (wcslen(buffer
+ 1) + 1) * sizeof(WCHAR
)),
275 ret
= ce(hProcess
, buf_addr
, buffer
, user
);
281 for (j
= 0; j
< used_cache
; j
++) HeapFree(GetProcessHeap(), 0, (WCHAR
*)cache
[j
]);
282 HeapFree(GetProcessHeap(), 0, cache
);
287 static void DebugChannels_FillList(HWND hChannelLV
)
291 (void)ListView_DeleteAllItems(hChannelLV
);
293 hProcess
= OpenProcess(PROCESS_VM_OPERATION
| PROCESS_VM_READ
, FALSE
, get_selected_pid());
294 if (!hProcess
) return; /* FIXME messagebox */
295 SendMessageW(hChannelLV
, WM_SETREDRAW
, FALSE
, 0);
296 enum_channel(hProcess
, list_channel_CB
, (void*)hChannelLV
, TRUE
);
297 SendMessageW(hChannelLV
, WM_SETREDRAW
, TRUE
, 0);
298 CloseHandle(hProcess
);
301 static void DebugChannels_OnCreate(HWND hwndDlg
)
303 HWND hLV
= GetDlgItem(hwndDlg
, IDC_DEBUG_CHANNELS_LIST
);
306 lvc
.mask
= LVCF_FMT
| LVCF_TEXT
| LVCF_WIDTH
;
307 lvc
.fmt
= LVCFMT_LEFT
;
308 lvc
.pszText
= L
"Debug Channel";
310 (void)ListView_InsertColumn(hLV
, 0, &lvc
);
312 lvc
.mask
= LVCF_FMT
| LVCF_TEXT
| LVCF_WIDTH
;
313 lvc
.fmt
= LVCFMT_CENTER
;
314 lvc
.pszText
= L
"Fixme";
316 (void)ListView_InsertColumn(hLV
, 1, &lvc
);
318 lvc
.mask
= LVCF_FMT
| LVCF_TEXT
| LVCF_WIDTH
;
319 lvc
.fmt
= LVCFMT_CENTER
;
320 lvc
.pszText
= L
"Err";
322 (void)ListView_InsertColumn(hLV
, 2, &lvc
);
324 lvc
.mask
= LVCF_FMT
| LVCF_TEXT
| LVCF_WIDTH
;
325 lvc
.fmt
= LVCFMT_CENTER
;
326 lvc
.pszText
= L
"Warn";
328 (void)ListView_InsertColumn(hLV
, 3, &lvc
);
330 lvc
.mask
= LVCF_FMT
| LVCF_TEXT
| LVCF_WIDTH
;
331 lvc
.fmt
= LVCFMT_CENTER
;
332 lvc
.pszText
= L
"Trace";
334 (void)ListView_InsertColumn(hLV
, 4, &lvc
);
336 DebugChannels_FillList(hLV
);
339 static void DebugChannels_OnNotify(HWND hDlg
, LPARAM lParam
)
341 NMHDR
* nmh
= (NMHDR
*)lParam
;
346 if (nmh
->idFrom
== IDC_DEBUG_CHANNELS_LIST
)
351 NMITEMACTIVATE
* nmia
= (NMITEMACTIVATE
*)lParam
;
353 hProcess
= OpenProcess(PROCESS_VM_OPERATION
| PROCESS_VM_READ
| PROCESS_VM_WRITE
, FALSE
, get_selected_pid());
354 if (!hProcess
) return; /* FIXME message box */
355 lhti
.pt
= nmia
->ptAction
;
356 hChannelLV
= GetDlgItem(hDlg
, IDC_DEBUG_CHANNELS_LIST
);
357 SendMessageW(hChannelLV
, LVM_SUBITEMHITTEST
, 0, (LPARAM
)&lhti
);
358 if (nmia
->iSubItem
>= 1 && nmia
->iSubItem
<= 4)
362 unsigned bitmask
= 1 << (lhti
.iSubItem
- 1);
363 struct cce_user user
;
365 ListView_GetItemText(hChannelLV
, lhti
.iItem
, 0, name
, sizeof(name
) / sizeof(name
[0]));
366 ListView_GetItemText(hChannelLV
, lhti
.iItem
, lhti
.iSubItem
, val
, sizeof(val
) / sizeof(val
[0]));
368 user
.value
= (val
[0] == L
'x') ? 0 : bitmask
;
370 user
.done
= user
.notdone
= 0;
371 enum_channel(hProcess
, change_channel_CB
, &user
, FALSE
);
374 val
[0] ^= (L
'x' ^ L
' ');
375 ListView_SetItemText(hChannelLV
, lhti
.iItem
, lhti
.iSubItem
, val
);
378 printf("Some channel instance weren't correctly set\n");
380 CloseHandle(hProcess
);
386 static INT_PTR CALLBACK
DebugChannelsDlgProc(HWND hDlg
, UINT message
, WPARAM wParam
, LPARAM lParam
)
391 DebugChannels_OnCreate(hDlg
);
394 if (LOWORD(wParam
) == IDOK
|| LOWORD(wParam
) == IDCANCEL
) {
395 EndDialog(hDlg
, LOWORD(wParam
));
400 DebugChannels_OnNotify(hDlg
, lParam
);
406 void ProcessPage_OnDebugChannels(void)
408 DialogBoxW(hInst
, (LPCWSTR
)IDD_DEBUG_CHANNELS_DIALOG
, hMainWnd
, DebugChannelsDlgProc
);