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
23 #define WIN32_LEAN_AND_MEAN /* Exclude rarely-used stuff from Windows headers */
40 * - the dialog box could be non modal
42 * + could refresh channels from time to time
43 * + set the name of exec (and perhaps its pid) in dialog title
44 * - get a better UI (replace the 'x' by real tick boxes in list view)
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
49 * - use more global settings (like having a temporary on/off
50 * setting for a fixme:s or err:s
53 static DWORD (WINAPI
*pSymSetOptions
)(DWORD
);
54 static BOOL (WINAPI
*pSymInitialize
)(HANDLE
, PSTR
, BOOL
);
55 static DWORD (WINAPI
*pSymLoadModule
)(HANDLE
, HANDLE
, PSTR
, PSTR
, DWORD
, DWORD
);
56 static BOOL (WINAPI
*pSymCleanup
)(HANDLE
);
57 static BOOL (WINAPI
*pSymFromName
)(HANDLE
, LPSTR
, PSYMBOL_INFO
);
59 BOOL
AreDebugChannelsSupported(void)
61 static HANDLE hDbgHelp
/* = NULL */;
63 if (hDbgHelp
) return TRUE
;
65 if (!(hDbgHelp
= LoadLibrary("dbghelp.dll"))) return FALSE
;
66 pSymSetOptions
= (void*)GetProcAddress(hDbgHelp
, "SymSetOptions");
67 pSymInitialize
= (void*)GetProcAddress(hDbgHelp
, "SymInitialize");
68 pSymLoadModule
= (void*)GetProcAddress(hDbgHelp
, "SymLoadModule");
69 pSymFromName
= (void*)GetProcAddress(hDbgHelp
, "SymFromName");
70 pSymCleanup
= (void*)GetProcAddress(hDbgHelp
, "SymCleanup");
71 if (!pSymSetOptions
|| !pSymInitialize
|| !pSymLoadModule
|| !pSymCleanup
|| !pSymFromName
)
73 FreeLibrary(hDbgHelp
);
80 static DWORD
get_selected_pid(void)
86 for (Index
= 0; Index
< (ULONG
)ListView_GetItemCount(hProcessPageListCtrl
); Index
++)
88 memset(&lvitem
, 0, sizeof(LVITEM
));
90 lvitem
.mask
= LVIF_STATE
;
91 lvitem
.stateMask
= LVIS_SELECTED
;
94 ListView_GetItem(hProcessPageListCtrl
, &lvitem
);
96 if (lvitem
.state
& LVIS_SELECTED
)
100 dwProcessId
= PerfDataGetProcessId(Index
);
102 if ((ListView_GetSelectedCount(hProcessPageListCtrl
) != 1) || (dwProcessId
== 0))
107 static int list_channel_CB(HANDLE hProcess
, void* addr
, char* buffer
, void* user
)
113 HWND hChannelLV
= (HWND
)user
;
115 memset(&lvi
, 0, sizeof(lvi
));
117 lvi
.mask
= LVIF_TEXT
;
118 lvi
.pszText
= buffer
+ 1;
120 index
= ListView_InsertItem(hChannelLV
, &lvi
);
121 if (index
== -1) return 0;
124 for (j
= 0; j
< 4; j
++)
126 val
[0] = (buffer
[0] & (1 << j
)) ? 'x' : ' ';
127 ListView_SetItemText(hChannelLV
, index
, j
+ 1, val
);
134 const char* name
; /* channel to look for */
135 unsigned value
, mask
; /* how to change channel */
136 unsigned done
; /* number of successful changes */
137 unsigned notdone
; /* number of unsuccessful changes */
140 /******************************************************************
143 * Callback used for changing a given channel attributes
145 static int change_channel_CB(HANDLE hProcess
, void* addr
, char* buffer
, void* pmt
)
147 struct cce_user
* user
= (struct cce_user
*)pmt
;
149 if (!user
->name
|| !strcmp(buffer
+ 1, user
->name
))
151 buffer
[0] = (buffer
[0] & ~user
->mask
) | (user
->value
& user
->mask
);
152 if (WriteProcessMemory(hProcess
, addr
, buffer
, 1, NULL
))
160 void* get_symbol(HANDLE hProcess
, char* name
, char* lib
)
162 char buffer
[sizeof(IMAGEHLP_SYMBOL
) + 256];
163 SYMBOL_INFO
* si
= (SYMBOL_INFO
*)buffer
;
166 pSymSetOptions(SYMOPT_DEFERRED_LOADS
| SYMOPT_PUBLICS_ONLY
);
167 /* FIXME: the TRUE option is due to the face that dbghelp requires it
168 * when loading an ELF module
170 if (pSymInitialize(hProcess
, NULL
, TRUE
))
172 si
->SizeOfStruct
= sizeof(*si
);
173 si
->MaxNameLen
= sizeof(buffer
) - sizeof(IMAGEHLP_SYMBOL
);
174 if (pSymLoadModule(hProcess
, NULL
, lib
, NULL
, 0, 0) &&
175 pSymFromName(hProcess
, name
, si
))
176 ret
= (void*)(ULONG_PTR
)si
->Address
;
177 pSymCleanup(hProcess
);
182 struct dll_option_layout
186 char* const* channels
;
190 typedef int (*EnumChannelCB
)(HANDLE
, void*, char*, void*);
192 /******************************************************************
195 * Enumerates all known channels on process hProcess through callback
198 static int enum_channel(HANDLE hProcess
, EnumChannelCB ce
, void* user
, unsigned unique
)
200 struct dll_option_layout dol
;
204 unsigned char buffer
[32];
206 const char** cache
= NULL
;
207 unsigned num_cache
, used_cache
;
209 if (!(addr
= get_symbol(hProcess
, "first_dll", "libwine.so"))) return -1;
211 cache
= HeapAlloc(GetProcessHeap(), 0, (num_cache
= 32) * sizeof(char*));
217 ret
&& addr
&& ReadProcessMemory(hProcess
, addr
, &dol
, sizeof(dol
), NULL
);
220 for (i
= 0; i
< dol
.nb_channels
; i
++)
222 if (ReadProcessMemory(hProcess
, (void*)(dol
.channels
+ i
), &buf_addr
, sizeof(buf_addr
), NULL
) &&
223 ReadProcessMemory(hProcess
, buf_addr
, buffer
, sizeof(buffer
), NULL
))
227 /* since some channels are defined in multiple compilation units,
228 * they will appear several times...
229 * so cache the channel's names we already reported and don't report
232 for (j
= 0; j
< used_cache
; j
++)
233 if (!strcmp(cache
[j
], buffer
+ 1)) break;
234 if (j
!= used_cache
) continue;
235 if (used_cache
== num_cache
)
236 cache
= HeapReAlloc(GetProcessHeap(), 0, cache
, (num_cache
*= 2) * sizeof(char*));
237 cache
[used_cache
++] = strcpy(HeapAlloc(GetProcessHeap(), 0, strlen(buffer
+ 1) + 1),
240 ret
= ce(hProcess
, buf_addr
, buffer
, user
);
246 for (j
= 0; j
< used_cache
; j
++) HeapFree(GetProcessHeap(), 0, (char*)cache
[j
]);
247 HeapFree(GetProcessHeap(), 0, cache
);
252 static void DebugChannels_FillList(HWND hChannelLV
)
256 ListView_DeleteAllItems(hChannelLV
);
258 hProcess
= OpenProcess(PROCESS_VM_OPERATION
| PROCESS_VM_READ
, FALSE
, get_selected_pid());
259 if (!hProcess
) return; /* FIXME messagebox */
260 SendMessage(hChannelLV
, WM_SETREDRAW
, FALSE
, 0);
261 enum_channel(hProcess
, list_channel_CB
, (void*)hChannelLV
, TRUE
);
262 SendMessage(hChannelLV
, WM_SETREDRAW
, TRUE
, 0);
263 CloseHandle(hProcess
);
266 static void DebugChannels_OnCreate(HWND hwndDlg
)
268 HWND hLV
= GetDlgItem(hwndDlg
, IDC_DEBUG_CHANNELS_LIST
);
271 lvc
.mask
= LVCF_FMT
| LVCF_TEXT
| LVCF_WIDTH
;
272 lvc
.fmt
= LVCFMT_LEFT
;
273 lvc
.pszText
= _T("Debug Channel");
275 ListView_InsertColumn(hLV
, 0, &lvc
);
277 lvc
.mask
= LVCF_FMT
| LVCF_TEXT
| LVCF_WIDTH
;
278 lvc
.fmt
= LVCFMT_CENTER
;
279 lvc
.pszText
= _T("Fixme");
281 ListView_InsertColumn(hLV
, 1, &lvc
);
283 lvc
.mask
= LVCF_FMT
| LVCF_TEXT
| LVCF_WIDTH
;
284 lvc
.fmt
= LVCFMT_CENTER
;
285 lvc
.pszText
= _T("Err");
287 ListView_InsertColumn(hLV
, 2, &lvc
);
289 lvc
.mask
= LVCF_FMT
| LVCF_TEXT
| LVCF_WIDTH
;
290 lvc
.fmt
= LVCFMT_CENTER
;
291 lvc
.pszText
= _T("Warn");
293 ListView_InsertColumn(hLV
, 3, &lvc
);
295 lvc
.mask
= LVCF_FMT
| LVCF_TEXT
| LVCF_WIDTH
;
296 lvc
.fmt
= LVCFMT_CENTER
;
297 lvc
.pszText
= _T("Trace");
299 ListView_InsertColumn(hLV
, 4, &lvc
);
301 DebugChannels_FillList(hLV
);
304 static void DebugChannels_OnNotify(HWND hDlg
, LPARAM lParam
)
306 NMHDR
* nmh
= (NMHDR
*)lParam
;
311 if (nmh
->idFrom
== IDC_DEBUG_CHANNELS_LIST
)
316 NMITEMACTIVATE
* nmia
= (NMITEMACTIVATE
*)lParam
;
318 hProcess
= OpenProcess(PROCESS_VM_OPERATION
| PROCESS_VM_READ
| PROCESS_VM_WRITE
, FALSE
, get_selected_pid());
319 if (!hProcess
) return; /* FIXME message box */
320 lhti
.pt
= nmia
->ptAction
;
321 hChannelLV
= GetDlgItem(hDlg
, IDC_DEBUG_CHANNELS_LIST
);
322 SendMessage(hChannelLV
, LVM_SUBITEMHITTEST
, 0, (LPARAM
)&lhti
);
323 if (nmia
->iSubItem
>= 1 && nmia
->iSubItem
<= 4)
327 unsigned bitmask
= 1 << (lhti
.iSubItem
- 1);
328 struct cce_user user
;
330 ListView_GetItemText(hChannelLV
, lhti
.iItem
, 0, name
, sizeof(name
) / sizeof(name
[0]));
331 ListView_GetItemText(hChannelLV
, lhti
.iItem
, lhti
.iSubItem
, val
, sizeof(val
) / sizeof(val
[0]));
333 user
.value
= (val
[0] == 'x') ? 0 : bitmask
;
335 user
.done
= user
.notdone
= 0;
336 enum_channel(hProcess
, change_channel_CB
, &user
, FALSE
);
339 val
[0] ^= ('x' ^ ' ');
340 ListView_SetItemText(hChannelLV
, lhti
.iItem
, lhti
.iSubItem
, val
);
343 printf("Some channel instance weren't correctly set\n");
345 CloseHandle(hProcess
);
351 static LRESULT CALLBACK
DebugChannelsDlgProc(HWND hDlg
, UINT message
, WPARAM wParam
, LPARAM lParam
)
356 DebugChannels_OnCreate(hDlg
);
359 if (LOWORD(wParam
) == IDOK
|| LOWORD(wParam
) == IDCANCEL
)
361 EndDialog(hDlg
, LOWORD(wParam
));
366 DebugChannels_OnNotify(hDlg
, lParam
);
372 void ProcessPage_OnDebugChannels(void)
374 DialogBox(hInst
, (LPCTSTR
)IDD_DEBUG_CHANNELS_DIALOG
, hMainWnd
, (DLGPROC
)DebugChannelsDlgProc
);