dbgchnl.c 11.2 KB
Newer Older
1 2 3 4 5 6
/*
 *  ReactOS Task Manager
 *
 *  dbgchnl.c
 *
 *  Copyright (C) 2003 - 2004 Eric Pouech
7
 *  Copyright (C) 2008  Vladimir Pankratov
8 9 10 11 12 13 14 15 16 17 18 19 20
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
21
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
22
 */
23

24
#include <ctype.h>
25
#include <stdio.h>
26 27 28 29
#include <stdlib.h>

#include <windows.h>
#include <commctrl.h>
30 31
#include <winnt.h>
#include <dbghelp.h>
32

33 34 35
#include "taskmgr.h"
#include "perfdata.h"
#include "column.h"
36
#include "wine/debug.h"
37 38 39 40 41 42 43 44 45 46 47 48 49 50 51

/* TODO:
 *      - the dialog box could be non modal
 *      - in that case,
 *              + could refresh channels from time to time
 *              + set the name of exec (and perhaps its pid) in dialog title
 *      - get a better UI (replace the 'x' by real tick boxes in list view)
 *      - enhance visual feedback: the list is large, and it's hard to get the
 *        right line when clicking on rightmost column (trace for example)
 *      - get rid of printfs (error reporting) and use real message boxes
 *      - include the column width settings in the full column management scheme
 *      - use more global settings (like having a temporary on/off
 *        setting for a fixme:s or err:s
 */

52
static DWORD (WINAPI *pSymSetOptions)(DWORD);
53
static BOOL  (WINAPI *pSymInitialize)(HANDLE, PSTR, BOOL);
54
static DWORD (WINAPI *pSymLoadModule)(HANDLE, HANDLE, PCSTR, PCSTR, DWORD, DWORD);
55
static BOOL  (WINAPI *pSymCleanup)(HANDLE);
56
static BOOL  (WINAPI *pSymFromName)(HANDLE, PCSTR, PSYMBOL_INFO);
57 58 59 60 61

BOOL AreDebugChannelsSupported(void)
{
    static HANDLE   hDbgHelp /* = NULL */;

62 63
    static const WCHAR    wszDbgHelp[] = {'D','B','G','H','E','L','P','.','D','L','L',0};

64 65
    if (hDbgHelp) return TRUE;

66
    if (!(hDbgHelp = LoadLibraryW(wszDbgHelp))) return FALSE;
67
    pSymSetOptions = (void*)GetProcAddress(hDbgHelp, "SymSetOptions");
68 69 70 71
    pSymInitialize = (void*)GetProcAddress(hDbgHelp, "SymInitialize");
    pSymLoadModule = (void*)GetProcAddress(hDbgHelp, "SymLoadModule");
    pSymFromName   = (void*)GetProcAddress(hDbgHelp, "SymFromName");
    pSymCleanup    = (void*)GetProcAddress(hDbgHelp, "SymCleanup");
72
    if (!pSymSetOptions || !pSymInitialize || !pSymLoadModule || !pSymCleanup || !pSymFromName)
73 74 75 76 77 78 79 80 81 82
    {
        FreeLibrary(hDbgHelp);
        hDbgHelp = NULL;
        return FALSE;
    }
    return TRUE;
}

static DWORD    get_selected_pid(void)
{
83 84
    LVITEMW     lvitem;
    ULONG       Index, Count;
85 86
    DWORD       dwProcessId;

87 88
    Count = SendMessageW(hProcessPageListCtrl, LVM_GETITEMCOUNT, 0, 0);
    for (Index = 0; Index < Count; Index++)
89 90 91 92
    {
        lvitem.mask = LVIF_STATE;
        lvitem.stateMask = LVIS_SELECTED;
        lvitem.iItem = Index;
93
        lvitem.iSubItem = 0;
94

95
        SendMessageW(hProcessPageListCtrl, LVM_GETITEMW, 0, (LPARAM) &lvitem);
96 97 98 99 100

        if (lvitem.state & LVIS_SELECTED)
            break;
    }

101
    Count = SendMessageW(hProcessPageListCtrl, LVM_GETSELECTEDCOUNT, 0, 0);
102
    dwProcessId = PerfDataGetProcessId(Index);
103
    if ((Count != 1) || (dwProcessId == 0))
104 105 106 107
        return 0;
    return dwProcessId;
}

108
static int     list_channel_CB(HANDLE hProcess, void* addr, struct __wine_debug_channel* channel, void* user)
109 110
{
    int         j;
111 112
    WCHAR       nameW[sizeof(channel->name)], val[2];
    LVITEMW     lvitem;
113
    int         index;
114
    HWND        hChannelLV = user;
115

116 117
    MultiByteToWideChar(CP_ACP, 0, channel->name, sizeof(channel->name), nameW, sizeof(nameW)/sizeof(WCHAR));

118
    lvitem.mask = LVIF_TEXT;
119
    lvitem.pszText = nameW;
120 121
    lvitem.iItem = 0;
    lvitem.iSubItem = 0;
122

123
    index = ListView_InsertItemW(hChannelLV, &lvitem);
124 125 126 127 128
    if (index == -1) return 0;

    val[1] = '\0';
    for (j = 0; j < 4; j++)
    {
129
        val[0] = (channel->flags & (1 << j)) ? 'x' : ' ';
130
        ListView_SetItemTextW(hChannelLV, index, j + 1, val);
131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147
    }
    return 1;
}

struct cce_user
{
    const char* name;           /* channel to look for */
    unsigned    value, mask;    /* how to change channel */
    unsigned    done;           /* number of successful changes */
    unsigned    notdone;        /* number of unsuccessful changes */
};

/******************************************************************
 *		change_channel_CB
 *
 * Callback used for changing a given channel attributes
 */
148
static int change_channel_CB(HANDLE hProcess, void* addr, struct __wine_debug_channel *channel, void* pmt)
149 150 151
{
    struct cce_user* user = (struct cce_user*)pmt;

152
    if (!user->name || !strcmp(channel->name, user->name))
153
    {
154 155
        channel->flags = (channel->flags & ~user->mask) | (user->value & user->mask);
        if (WriteProcessMemory(hProcess, addr, channel, sizeof(*channel), NULL))
156 157 158 159 160 161 162
            user->done++;
        else
            user->notdone++;
    }
    return 1;
}

163
static void* get_symbol(HANDLE hProcess, const char* name)
164 165 166 167 168
{
    char                buffer[sizeof(IMAGEHLP_SYMBOL) + 256];
    SYMBOL_INFO*        si = (SYMBOL_INFO*)buffer;
    void*               ret = NULL;

169 170 171
    /* also ask for wine extensions (loading symbols from ELF files) */
    pSymSetOptions(SYMOPT_DEFERRED_LOADS | SYMOPT_PUBLICS_ONLY | 0x40000000);
    /* FIXME: the TRUE option is due to the fact that dbghelp requires it
172 173 174
     * when loading an ELF module
     */
    if (pSymInitialize(hProcess, NULL, TRUE))
175 176 177
    {
        si->SizeOfStruct = sizeof(*si);
        si->MaxNameLen = sizeof(buffer) - sizeof(IMAGEHLP_SYMBOL);
178
        if (pSymFromName(hProcess, name, si))
179
            ret = (void*)(ULONG_PTR)si->Address;
180 181 182 183 184
        pSymCleanup(hProcess);
    }
    return ret;
}

185
typedef int (*EnumChannelCB)(HANDLE, void*, struct __wine_debug_channel*, void*);
186 187 188 189 190 191 192

/******************************************************************
 *		enum_channel
 *
 * Enumerates all known channels on process hProcess through callback
 * ce.
 */
193
static int enum_channel(HANDLE hProcess, EnumChannelCB ce, void* user)
194
{
195 196
    struct __wine_debug_channel channel;
    int                         ret = 1;
197 198
    void*                       addr;

199
    if (!(addr = get_symbol(hProcess, "libwine.so.1!debug_options"))) return -1;
200

201
    while (ret && addr && ReadProcessMemory(hProcess, addr, &channel, sizeof(channel), NULL))
202
    {
203 204 205
        if (!channel.name[0]) break;
        ret = ce(hProcess, addr, &channel, user);
        addr = (struct __wine_debug_channel *)addr + 1;
206 207 208 209 210 211 212 213
    }
    return 0;
}

static void DebugChannels_FillList(HWND hChannelLV)
{
    HANDLE      hProcess;

214
    SendMessageW(hChannelLV, LVM_DELETEALLITEMS, 0, 0);
215

216
    hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_OPERATION | PROCESS_VM_READ, FALSE, get_selected_pid());
217
    if (!hProcess) return; /* FIXME messagebox */
218
    SendMessageW(hChannelLV, WM_SETREDRAW, FALSE, 0);
219
    enum_channel(hProcess, list_channel_CB, (void*)hChannelLV);
220
    SendMessageW(hChannelLV, WM_SETREDRAW, TRUE, 0);
221 222 223 224 225
    CloseHandle(hProcess);
}

static void DebugChannels_OnCreate(HWND hwndDlg)
{
226 227 228 229
    static WCHAR fixmeW[] = {'F','i','x','m','e','\0'};
    static WCHAR errW[]   = {'E','r','r','\0'};
    static WCHAR warnW[]  = {'W','a','r','n','\0'};
    static WCHAR traceW[] = {'T','r','a','c','e','\0'};
230
    HWND        hLV = GetDlgItem(hwndDlg, IDC_DEBUG_CHANNELS_LIST);
231
    LVCOLUMNW   lvc;
232 233 234
    WCHAR debug_channelW[255];

    LoadStringW(hInst, IDS_DEBUG_CHANNEL, debug_channelW, sizeof(debug_channelW)/sizeof(WCHAR));
235 236 237

    lvc.mask = LVCF_FMT | LVCF_TEXT | LVCF_WIDTH;
    lvc.fmt = LVCFMT_LEFT;
238
    lvc.pszText = debug_channelW;
239
    lvc.cx = 100;
240
    SendMessageW(hLV, LVM_INSERTCOLUMNW, 0, (LPARAM) &lvc);
241 242 243

    lvc.mask = LVCF_FMT | LVCF_TEXT | LVCF_WIDTH;
    lvc.fmt = LVCFMT_CENTER;
244
    lvc.pszText = fixmeW;
245
    lvc.cx = 55;
246
    SendMessageW(hLV, LVM_INSERTCOLUMNW, 1, (LPARAM) &lvc);
247 248 249

    lvc.mask = LVCF_FMT | LVCF_TEXT | LVCF_WIDTH;
    lvc.fmt = LVCFMT_CENTER;
250
    lvc.pszText = errW;
251
    lvc.cx = 55;
252
    SendMessageW(hLV, LVM_INSERTCOLUMNW, 2, (LPARAM) &lvc);
253 254 255

    lvc.mask = LVCF_FMT | LVCF_TEXT | LVCF_WIDTH;
    lvc.fmt = LVCFMT_CENTER;
256
    lvc.pszText = warnW;
257
    lvc.cx = 55;
258
    SendMessageW(hLV, LVM_INSERTCOLUMNW, 3, (LPARAM) &lvc);
259 260 261

    lvc.mask = LVCF_FMT | LVCF_TEXT | LVCF_WIDTH;
    lvc.fmt = LVCFMT_CENTER;
262
    lvc.pszText = traceW;
263
    lvc.cx = 55;
264
    SendMessageW(hLV, LVM_INSERTCOLUMNW, 4, (LPARAM) &lvc);
265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286

    DebugChannels_FillList(hLV);
}

static void DebugChannels_OnNotify(HWND hDlg, LPARAM lParam)
{
    NMHDR*      nmh = (NMHDR*)lParam;

    switch (nmh->code)
    {
    case NM_CLICK:
        if (nmh->idFrom == IDC_DEBUG_CHANNELS_LIST)
        {
            LVHITTESTINFO       lhti;
            HWND                hChannelLV;
            HANDLE              hProcess;
            NMITEMACTIVATE*     nmia = (NMITEMACTIVATE*)lParam;

            hProcess = OpenProcess(PROCESS_VM_OPERATION | PROCESS_VM_READ | PROCESS_VM_WRITE, FALSE, get_selected_pid());
            if (!hProcess) return; /* FIXME message box */
            lhti.pt = nmia->ptAction;
            hChannelLV = GetDlgItem(hDlg, IDC_DEBUG_CHANNELS_LIST);
287
            SendMessageW(hChannelLV, LVM_SUBITEMHITTEST, 0, (LPARAM)&lhti);
288 289
            if (nmia->iSubItem >= 1 && nmia->iSubItem <= 4)
            {
290 291
                WCHAR           val[2];
                char            name[32];
292 293 294
                unsigned        bitmask = 1 << (lhti.iSubItem - 1);
                struct cce_user user;

295 296
                ListView_GetItemTextA(hChannelLV, lhti.iItem, 0, name, sizeof(name) / sizeof(name[0]));
                ListView_GetItemTextW(hChannelLV, lhti.iItem, lhti.iSubItem, val, sizeof(val) / sizeof(val[0]));
297 298 299 300
                user.name = name;
                user.value = (val[0] == 'x') ? 0 : bitmask;
                user.mask = bitmask;
                user.done = user.notdone = 0;
301
                enum_channel(hProcess, change_channel_CB, &user);
302 303 304
                if (user.done)
                {
                    val[0] ^= ('x' ^ ' ');
305
                    ListView_SetItemTextW(hChannelLV, lhti.iItem, lhti.iSubItem, val);
306 307
                }
                if (user.notdone)
308
                    printf("Some channel instances weren't correctly set\n");
309 310 311 312 313 314 315
            }
            CloseHandle(hProcess);
        }
        break;
    }
}

316
static INT_PTR CALLBACK DebugChannelsDlgProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338
{
    switch (message)
    {
    case WM_INITDIALOG:
        DebugChannels_OnCreate(hDlg);
        return TRUE;
    case WM_COMMAND:
        if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)
        {
            EndDialog(hDlg, LOWORD(wParam));
            return TRUE;
        }
        break;
    case WM_NOTIFY:
        DebugChannels_OnNotify(hDlg, lParam);
        break;
    }
    return FALSE;
}

void ProcessPage_OnDebugChannels(void)
{
339
    DialogBoxW(hInst, (LPCWSTR)IDD_DEBUG_CHANNELS_DIALOG, hMainWnd, DebugChannelsDlgProc);
340
}