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
40 * - the dialog box could be non modal
42 * + could refresh channels from time to time
43 * - get a better UI (replace the 'x' by real tick boxes in list view)
44 * - implement a real solution around the get_symbol hack
45 * - enhance visual feedback: the list is large, and it's hard to get the
46 * right line when clicking on rightmost column (trace for example)
47 * - get rid of printfs (error reporting) and use real message boxes
48 * - include the column width settings in the full column management scheme
51 BOOL
DebugChannelsAreSupported(void)
59 static DWORD
get_selected_pid(void)
65 for (Index
=0; Index
<(ULONG
)ListView_GetItemCount(hProcessPageListCtrl
); Index
++)
67 memset(&lvitem
, 0, sizeof(LVITEM
));
69 lvitem
.mask
= LVIF_STATE
;
70 lvitem
.stateMask
= LVIS_SELECTED
;
73 ListView_GetItem(hProcessPageListCtrl
, &lvitem
);
75 if (lvitem
.state
& LVIS_SELECTED
)
79 dwProcessId
= PerfDataGetProcessId(Index
);
81 if ((ListView_GetSelectedCount(hProcessPageListCtrl
) != 1) || (dwProcessId
== 0))
86 static int list_channel_CB(HANDLE hProcess
, void* addr
, char* buffer
, void* user
)
92 HWND hChannelLV
= (HWND
)user
;
94 memset(&lvi
, 0, sizeof(lvi
));
97 lvi
.pszText
= buffer
+ 1;
99 index
= ListView_InsertItem(hChannelLV
, &lvi
);
100 if (index
== -1) return 0;
103 for (j
= 0; j
< 4; j
++)
105 val
[0] = (buffer
[0] & (1 << j
)) ? 'x' : ' ';
106 ListView_SetItemText(hChannelLV
, index
, j
+ 1, val
);
113 const char* name
; /* channel to look for */
114 unsigned value
, mask
; /* how to change channel */
115 unsigned done
; /* number of successful changes */
116 unsigned notdone
; /* number of unsuccessful changes */
119 /******************************************************************
122 * Callback used for changing a given channel attributes
124 static int change_channel_CB(HANDLE hProcess
, void* addr
, char* buffer
, void* pmt
)
126 struct cce_user
* user
= (struct cce_user
*)pmt
;
128 if (!user
->name
|| !strcmp(buffer
+ 1, user
->name
))
130 buffer
[0] = (buffer
[0] & ~user
->mask
) | (user
->value
& user
->mask
);
131 if (WriteProcessMemory(hProcess
, addr
, buffer
, 1, NULL
))
140 /******************************************************************
143 * Here it gets ugly :-(
144 * This is quick hack to get the address of first_dll in a running process
145 * We make the following assumptions:
146 * - libwine (lib) is loaded in all processes at the same address (or
147 * at least at the same address at this process)
148 * - we load the same libwine.so version in this process and in the
150 * Final address is gotten by: 1/ querying the address of a known exported
151 * symbol out of libwine.so with dlsym, 2/ then querying nm on libwine.so to
152 * get the offset from the data segment of this known symbol and of first_dll,
153 * 3/ computing the actual address of first_dll by adding the result of 1/ and
155 * Ugly, yes, but it somehow works. We should replace that with debughlp
156 * library, that'd be way better. Exporting first_dll from libwine.so would make
157 * this code simpler, but still ugly.
159 /* FIXME: we only need those includes for the next function */
160 #include <dlfcn.h> /* for RTLD_LAZY */
161 #include <sys/types.h>
162 #include <sys/stat.h>
165 #include "wine/library.h"
167 void* get_symbol(HANDLE hProcess
, const char* name
, const char* lib
)
171 DWORD addr
= 0, tmp
= 0;
175 if (!(h
= wine_dlopen(lib
, RTLD_LAZY
, buffer
, sizeof(buffer
))))
177 printf("Couldn't load %s (%s)\n", lib
, buffer
);
181 env
= getenv("LD_LIBRARY_PATH");
187 for (ptr
= env
= strdup(env
); ptr
; ptr
= next
)
189 next
= strchr(ptr
, ':');
190 if (next
) *next
++ = '\0';
191 sprintf(buffer
, "nm %s", ptr
);
192 if (buffer
[strlen(buffer
) - 1] != '/') strcat(buffer
, "/");
194 if (stat(buffer
+ 3, &s
) == 0) break;
199 printf("Couldn't find %s in LD_LIBRARY_PATH\n", lib
);
203 if (!(f
= popen(buffer
, "r")))
205 printf("Cannot execute '%s'\n", buffer
);
209 while (fgets(buffer
, sizeof(buffer
), f
))
211 char *p
= buffer
+ strlen(buffer
) - 1;
212 if (p
< buffer
) continue;
213 if (*p
== '\n') *p
-- = 0;
214 if (p
- buffer
< 11) continue;
216 if (!strcmp(&buffer
[11], name
)) addr
+= strtol(buffer
, NULL
, 16);
217 if (buffer
[9] == 'D' && !tmp
&& (tmp
= (DWORD
)wine_dlsym(h
, &buffer
[11], NULL
, 0)) != 0)
218 addr
+= tmp
- strtol(buffer
, NULL
, 16);
224 void* get_symbol(HANDLE hProcess
, const char* name
, const char* lib
)
226 printf("get_symbol: not implemented on this platform\n");
231 struct dll_option_layout
235 char* const* channels
;
239 typedef int (*EnumChannelCB
)(HANDLE
, void*, char*, void*);
241 /******************************************************************
244 * Enumerates all known channels on process hProcess through callback
247 static int enum_channel(HANDLE hProcess
, EnumChannelCB ce
, void* user
, unsigned unique
)
249 struct dll_option_layout dol
;
254 const char** cache
= NULL
;
255 unsigned num_cache
, used_cache
;
257 addr
= get_symbol(hProcess
, "first_dll", "libwine.so");
258 if (!addr
) return -1;
260 cache
= HeapAlloc(GetProcessHeap(), 0, (num_cache
= 32) * sizeof(char*));
266 ret
&& addr
&& ReadProcessMemory(hProcess
, addr
, &dol
, sizeof(dol
), NULL
);
269 for (i
= 0; i
< dol
.nb_channels
; i
++)
271 if (ReadProcessMemory(hProcess
, (void*)(dol
.channels
+ i
), &buf_addr
, sizeof(buf_addr
), NULL
) &&
272 ReadProcessMemory(hProcess
, buf_addr
, buffer
, sizeof(buffer
), NULL
))
276 /* since some channels are defined in multiple compilation units,
277 * they will appear several times...
278 * so cache the channel's names we already reported and don't report
281 for (j
= 0; j
< used_cache
; j
++)
282 if (!strcmp(cache
[j
], buffer
+ 1)) break;
283 if (j
!= used_cache
) continue;
284 if (used_cache
== num_cache
)
285 cache
= HeapReAlloc(GetProcessHeap(), 0, cache
, (num_cache
*= 2) * sizeof(char*));
286 cache
[used_cache
++] = strcpy(HeapAlloc(GetProcessHeap(), 0, strlen(buffer
+ 1) + 1),
289 ret
= ce(hProcess
, buf_addr
, buffer
, user
);
295 for (j
= 0; j
< used_cache
; j
++) HeapFree(GetProcessHeap(), 0, (char*)cache
[j
]);
296 HeapFree(GetProcessHeap(), 0, cache
);
301 static void DebugChannels_FillList(HWND hChannelLV
)
305 ListView_DeleteAllItems(hChannelLV
);
307 hProcess
= OpenProcess(PROCESS_VM_OPERATION
| PROCESS_VM_READ
, FALSE
, get_selected_pid());
308 if (!hProcess
) return; /* FIXME messagebox */
309 SendMessage(hChannelLV
, WM_SETREDRAW
, FALSE
, 0);
310 enum_channel(hProcess
, list_channel_CB
, (void*)hChannelLV
, TRUE
);
311 SendMessage(hChannelLV
, WM_SETREDRAW
, TRUE
, 0);
312 CloseHandle(hProcess
);
315 static void DebugChannels_OnCreate(HWND hwndDlg
)
317 HWND hLV
= GetDlgItem(hwndDlg
, IDC_DEBUG_CHANNELS_LIST
);
320 lvc
.mask
= LVCF_FMT
| LVCF_TEXT
| LVCF_WIDTH
;
321 lvc
.fmt
= LVCFMT_LEFT
;
322 lvc
.pszText
= _T("Debug Channel");
324 ListView_InsertColumn(hLV
, 0, &lvc
);
326 lvc
.mask
= LVCF_FMT
| LVCF_TEXT
| LVCF_WIDTH
;
327 lvc
.fmt
= LVCFMT_CENTER
;
328 lvc
.pszText
= _T("Fixme");
330 ListView_InsertColumn(hLV
, 1, &lvc
);
332 lvc
.mask
= LVCF_FMT
| LVCF_TEXT
| LVCF_WIDTH
;
333 lvc
.fmt
= LVCFMT_CENTER
;
334 lvc
.pszText
= _T("Err");
336 ListView_InsertColumn(hLV
, 2, &lvc
);
338 lvc
.mask
= LVCF_FMT
| LVCF_TEXT
| LVCF_WIDTH
;
339 lvc
.fmt
= LVCFMT_CENTER
;
340 lvc
.pszText
= _T("Warn");
342 ListView_InsertColumn(hLV
, 3, &lvc
);
344 lvc
.mask
= LVCF_FMT
| LVCF_TEXT
| LVCF_WIDTH
;
345 lvc
.fmt
= LVCFMT_CENTER
;
346 lvc
.pszText
= _T("Trace");
348 ListView_InsertColumn(hLV
, 4, &lvc
);
350 DebugChannels_FillList(hLV
);
353 static void DebugChannels_OnNotify(HWND hDlg
, LPARAM lParam
)
355 NMHDR
* nmh
= (NMHDR
*)lParam
;
360 if (nmh
->idFrom
== IDC_DEBUG_CHANNELS_LIST
)
365 NMITEMACTIVATE
* nmia
= (NMITEMACTIVATE
*)lParam
;
367 hProcess
= OpenProcess(PROCESS_VM_OPERATION
| PROCESS_VM_READ
| PROCESS_VM_WRITE
, FALSE
, get_selected_pid());
368 if (!hProcess
) return; /* FIXME message box */
369 lhti
.pt
= nmia
->ptAction
;
370 hChannelLV
= GetDlgItem(hDlg
, IDC_DEBUG_CHANNELS_LIST
);
371 SendMessage(hChannelLV
, LVM_SUBITEMHITTEST
, 0, (LPARAM
)&lhti
);
372 if (nmia
->iSubItem
>= 1 && nmia
->iSubItem
<= 4)
376 unsigned bitmask
= 1 << (lhti
.iSubItem
- 1);
377 struct cce_user user
;
379 ListView_GetItemText(hChannelLV
, lhti
.iItem
, 0, name
, sizeof(name
) / sizeof(name
[0]));
380 ListView_GetItemText(hChannelLV
, lhti
.iItem
, lhti
.iSubItem
, val
, sizeof(val
) / sizeof(val
[0]));
382 user
.value
= (val
[0] == 'x') ? 0 : bitmask
;
384 user
.done
= user
.notdone
= 0;
385 enum_channel(hProcess
, change_channel_CB
, &user
, FALSE
);
388 val
[0] ^= ('x' ^ ' ');
389 ListView_SetItemText(hChannelLV
, lhti
.iItem
, lhti
.iSubItem
, val
);
392 printf("Some channel instance weren't correctly set\n");
394 CloseHandle(hProcess
);
400 static INT_PTR CALLBACK
DebugChannelsDlgProc(HWND hDlg
, UINT message
, WPARAM wParam
, LPARAM lParam
)
405 DebugChannels_OnCreate(hDlg
);
408 if (LOWORD(wParam
) == IDOK
|| LOWORD(wParam
) == IDCANCEL
) {
409 EndDialog(hDlg
, LOWORD(wParam
));
414 DebugChannels_OnNotify(hDlg
, lParam
);
420 void ProcessPage_OnDebugChannels(void)
422 DialogBox(hInst
, (LPCTSTR
)IDD_DEBUG_CHANNELS_DIALOG
, hMainWnd
, DebugChannelsDlgProc
);