dbgchnl.c 11.3 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 25
 */
    
#define WIN32_LEAN_AND_MEAN    /* Exclude rarely-used stuff from Windows headers */
#include <windows.h>
26
#include <ctype.h>
27 28 29 30 31 32 33 34 35 36
#include <commctrl.h>
#include <stdlib.h>
#include <memory.h>
#include <stdio.h>
#include <winnt.h>
#include <dbghelp.h>
    
#include "taskmgr.h"
#include "perfdata.h"
#include "column.h"
37
#include "wine/debug.h"
38 39 40 41 42 43 44 45 46 47 48 49 50 51 52

/* 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
 */

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

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

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

65 66
    if (hDbgHelp) return TRUE;

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

static DWORD    get_selected_pid(void)
{
    LVITEM      lvitem;
    ULONG       Index;
    DWORD       dwProcessId;

    for (Index = 0; Index < (ULONG)ListView_GetItemCount(hProcessPageListCtrl); Index++)
    {
        lvitem.mask = LVIF_STATE;
        lvitem.stateMask = LVIS_SELECTED;
        lvitem.iItem = Index;
93
        lvitem.iSubItem = 0;
94

95
        SendMessage(hProcessPageListCtrl, LVM_GETITEM, 0, (LPARAM) &lvitem);
96 97 98 99 100 101 102 103 104 105 106 107

        if (lvitem.state & LVIS_SELECTED)
            break;
    }

    dwProcessId = PerfDataGetProcessId(Index);

    if ((ListView_GetSelectedCount(hProcessPageListCtrl) != 1) || (dwProcessId == 0))
        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
    WCHAR       val[2];
112
    LVITEMA     lvitem;
113
    int         index;
114
    HWND        hChannelLV = user;
115

116 117 118 119
    lvitem.mask = LVIF_TEXT;
    lvitem.pszText = channel->name;
    lvitem.iItem = 0;
    lvitem.iSubItem = 0;
120

121
    index = ListView_InsertItem(hChannelLV, &lvitem);
122 123 124 125 126
    if (index == -1) return 0;

    val[1] = '\0';
    for (j = 0; j < 4; j++)
    {
127
        val[0] = (channel->flags & (1 << j)) ? 'x' : ' ';
128
        ListView_SetItemTextW(hChannelLV, index, j + 1, val);
129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145
    }
    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
 */
146
static int change_channel_CB(HANDLE hProcess, void* addr, struct __wine_debug_channel *channel, void* pmt)
147 148 149
{
    struct cce_user* user = (struct cce_user*)pmt;

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

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

167 168 169
    /* 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
170 171 172
     * when loading an ELF module
     */
    if (pSymInitialize(hProcess, NULL, TRUE))
173 174 175
    {
        si->SizeOfStruct = sizeof(*si);
        si->MaxNameLen = sizeof(buffer) - sizeof(IMAGEHLP_SYMBOL);
176
        if (pSymFromName(hProcess, name, si))
177
            ret = (void*)(ULONG_PTR)si->Address;
178 179 180 181 182
        pSymCleanup(hProcess);
    }
    return ret;
}

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

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

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

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

static void DebugChannels_FillList(HWND hChannelLV)
{
    HANDLE      hProcess;

212
    SendMessage(hChannelLV, LVM_DELETEALLITEMS, 0, 0);
213

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

static void DebugChannels_OnCreate(HWND hwndDlg)
{
    HWND        hLV = GetDlgItem(hwndDlg, IDC_DEBUG_CHANNELS_LIST);
225 226
    LVCOLUMNW   lvc;

227 228 229 230 231 232 233 234 235 236 237
    WCHAR debug_channelW[255];
    WCHAR fixmeW[255];
    WCHAR errW[255];
    WCHAR warnW[255];
    WCHAR traceW[255];

    LoadStringW(hInst, IDS_DEBUG_CHANNEL, debug_channelW, sizeof(debug_channelW)/sizeof(WCHAR));
    LoadStringW(hInst, IDS_DEBUG_CHANNEL_FIXME, fixmeW, sizeof(fixmeW)/sizeof(WCHAR));
    LoadStringW(hInst, IDS_DEBUG_CHANNEL_ERR, errW, sizeof(errW)/sizeof(WCHAR));
    LoadStringW(hInst, IDS_DEBUG_CHANNEL_WARN, warnW, sizeof(warnW)/sizeof(WCHAR));
    LoadStringW(hInst, IDS_DEBUG_CHANNEL_TRACE, traceW, sizeof(traceW)/sizeof(WCHAR));
238 239 240

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

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

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

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

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

    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);
            SendMessage(hChannelLV, LVM_SUBITEMHITTEST, 0, (LPARAM)&lhti);
            if (nmia->iSubItem >= 1 && nmia->iSubItem <= 4)
            {
293 294
                WCHAR           val[2];
                char            name[32];
295 296 297
                unsigned        bitmask = 1 << (lhti.iSubItem - 1);
                struct cce_user user;

298 299
                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]));
300 301 302 303
                user.name = name;
                user.value = (val[0] == 'x') ? 0 : bitmask;
                user.mask = bitmask;
                user.done = user.notdone = 0;
304
                enum_channel(hProcess, change_channel_CB, &user);
305 306 307
                if (user.done)
                {
                    val[0] ^= ('x' ^ ' ');
308
                    ListView_SetItemTextW(hChannelLV, lhti.iItem, lhti.iSubItem, val);
309 310
                }
                if (user.notdone)
311
                    printf("Some channel instances weren't correctly set\n");
312 313 314 315 316 317 318
            }
            CloseHandle(hProcess);
        }
        break;
    }
}

319
static INT_PTR CALLBACK DebugChannelsDlgProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341
{
    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)
{
342
    DialogBoxW(hInst, (LPCWSTR)IDD_DEBUG_CHANNELS_DIALOG, hMainWnd, DebugChannelsDlgProc);
343
}