newlib-cygwin/winsup/cygwin/strace.cc

516 lines
14 KiB
C++

/* strace.cc: system/windows tracing
This file is part of Cygwin.
This software is a copyrighted work licensed under the terms of the
Cygwin license. Please consult the file "CYGWIN_LICENSE" for
details. */
#include "winsup.h"
#include <ctype.h>
#include "cygerrno.h"
#include "pinfo.h"
#include "perprocess.h"
#include "cygwin_version.h"
#include "cygthread.h"
#include "path.h"
#include "fhandler.h"
#include "dtable.h"
#include "cygheap.h"
#include "child_info.h"
#include "sync.h"
#define PROTECT(x) {x[NT_MAX_PATH - 1] = '\0';}
#define CHECK(x) if (x[NT_MAX_PATH - 1] != '\0') \
{ small_printf ("array bound exceeded %d\n", __LINE__); \
ExitProcess (1); \
}
class strace NO_COPY strace;
#ifndef NOSTRACE
void
strace::activate (bool isfork)
{
if (!_active && being_debugged ())
{
char buf[30];
__small_sprintf (buf, "cYg%8x %lx %d",
_STRACE_INTERFACE_ACTIVATE_ADDR, &_active, isfork);
OutputDebugString (buf);
if (_active)
{
char pidbuf[80];
PWCHAR progname;
if (myself)
{
__small_sprintf (pidbuf, "(pid %d, ppid %d, windows pid %u)",
myself->pid, myself->ppid ?: 1,
GetCurrentProcessId ());
progname = myself->progname;
}
else
{
__small_sprintf (pidbuf, "(windows pid %u)",
GetCurrentProcessId ());
progname = global_progname;
}
prntf (1, NULL, "**********************************************");
prntf (1, NULL, "Program name: %W %s", progname, pidbuf);
prntf (1, NULL, "OS version: Windows %s", wincap.osname ());
prntf (1, NULL, "**********************************************");
}
}
}
void
strace::dll_info ()
{
if (active ())
{
prntf (1, NULL, "App version: %d.%d, api: %d.%d",
user_data->dll_major, user_data->dll_minor,
user_data->api_major, user_data->api_minor);
prntf (1, NULL, "DLL version: %d.%d, api: %d.%d",
cygwin_version.dll_major, cygwin_version.dll_minor,
cygwin_version.api_major, cygwin_version.api_minor);
prntf (1, NULL, "DLL build: %s", cygwin_version.dll_build_date);
}
}
int
strace::microseconds ()
{
static LONGLONG process_start NO_COPY;
clk_monotonic_t *clk = (clk_monotonic_t *) get_clock (CLOCK_MONOTONIC);
if (!process_start)
process_start = clk->strace_usecs ();
return (int) (clk->strace_usecs () - process_start);
}
static int __stdcall
getfunc (char *in_dst, const char *func)
{
const char *p;
const char *pe;
char *dst = in_dst;
for (p = func; (pe = strchr (p, '(')); p = pe + 1)
if (isalnum ((int)pe[-1]) || pe[-1] == '_')
break;
else if (isspace ((int)pe[-1]))
{
pe--;
break;
}
if (!pe)
pe = strchr (func, '\0');
for (p = pe; p > func; p--)
if (p != pe && *p == ' ')
{
p++;
break;
}
if (*p == '*')
p++;
while (p < pe)
*dst++ = *p++;
*dst++ = ':';
*dst++ = ' ';
*dst = '\0';
return dst - in_dst;
}
static char *
mypid (char *buf)
{
if (myself && myself->pid)
__small_sprintf (buf, "%d", myself->pid);
else
__small_sprintf (buf, "(%d)", GetCurrentProcessId ());
return buf;
}
/* sprintf analog for use by output routines. */
int
strace::vsprntf (char *buf, const char *func, const char *infmt, va_list ap)
{
int count;
char fmt[80];
static NO_COPY bool nonewline = false;
DWORD err = GetLastError ();
const char *tn = mythreadname ();
int microsec = microseconds ();
lmicrosec = microsec;
__small_sprintf (fmt, "%7d [%s] %s ", microsec, tn, "%W %s%s");
SetLastError (err);
if (nonewline)
count = 0;
else
{
PWCHAR pn = NULL;
WCHAR progname[NAME_MAX];
if (cygwin_finished_initializing && __progname)
{
char *p = strrchr (__progname, '/');
if (p)
++p;
else
p = __progname;
char *pe = strrchr (p, '.');
if (!pe || !ascii_strcasematch (pe, ".exe"))
pe = strrchr (p, '\0');
sys_mbstowcs (pn = progname, NAME_MAX, p, pe - p);
}
else
{
PWCHAR p = wcsrchr (global_progname, L'\\');
++p;
PWCHAR pe = wcsrchr (p, '.');
if (!pe || wcscasecmp (pe, L".exe"))
pe = wcsrchr (p, L'\0');
pe = wcpncpy (progname, p, pe - p);
*pe = L'\0';
pn = progname;
}
char tmpbuf[20];
count = __small_sprintf (buf, fmt, pn, mypid (tmpbuf),
execing ? "!" : "");
if (func)
count += getfunc (buf + count, func);
}
count += __small_vsprintf (buf + count, infmt, ap);
char *p;
for (p = buf + count; p > buf; p--)
switch (p[-1])
{
case '\n':
p[-1] = '\0';
break;
case '\b':
*--p = '\0';
nonewline = true;
goto done;
default:
goto addnl;
}
addnl:
*p++ = '\n';
*p = '\0';
nonewline = false;
done:
return p - buf;
}
/* Write to strace file or strace queue. */
void
strace::write (unsigned category, const char *buf, int count)
{
# define PREFIX (3 + 8 + 1 + 8 + 1)
char outbuf[PREFIX + 1 + count + 1];
# define outstuff (outbuf + 12)
__small_sprintf (outstuff, "%x %s", category, buf);
__small_sprintf (outbuf, "cYg%08x", strlen (outstuff) + 1);
outstuff[-1] = ' ';
OutputDebugString (outbuf);
#undef outstuff
#undef PREFIX
}
void
strace::write_childpid (pid_t pid)
{
char buf[30];
if (!attached () || !being_debugged ())
return;
__small_sprintf (buf, "cYg%8x %x", _STRACE_CHILD_PID, pid);
OutputDebugString (buf);
}
/* Printf function used when tracing system calls.
Warning: DO NOT SET ERRNO HERE! */
static NO_COPY muto strace_buf_guard;
static NO_COPY char *buf;
void
strace::vprntf (unsigned category, const char *func, const char *fmt, va_list ap)
{
DWORD err = GetLastError ();
int len;
strace_buf_guard.init ("smallprint_buf")->acquire ();
/* Creating buffer on Windows process heap to drop stack pressure and
keeping our .bss small. */
if (!buf)
buf = (char *) HeapAlloc (GetProcessHeap (), 0, NT_MAX_PATH);
if (!buf)
return;
PROTECT (buf);
SetLastError (err);
len = vsprntf (buf, func, fmt, ap);
CHECK (buf);
if (category & _STRACE_SYSTEM)
{
DWORD done;
WriteFile (GetStdHandle (STD_ERROR_HANDLE), buf, len, &done, 0);
FlushFileBuffers (GetStdHandle (STD_ERROR_HANDLE));
/* Make sure that the message shows up on the screen, too, since this is
a serious error. */
if (GetFileType (GetStdHandle (STD_ERROR_HANDLE)) != FILE_TYPE_CHAR)
{
HANDLE h = CreateFile ("CONOUT$", GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE,
&sec_none, OPEN_EXISTING, 0, 0);
if (h != INVALID_HANDLE_VALUE)
{
WriteFile (h, buf, len, &done, 0);
CloseHandle (h);
}
}
#if 1 /* Experimental code */
/* PTY with pseudo console cannot display data written to
STD_ERROR_HANDLE (output_handle) if the process is cygwin
process. output_handle works only in native console apps.
Therefore the data should be written to output_handle_cyg
as well. */
fhandler_base *fh = ::cygheap->fdtab[2];
if (fh && fh->get_major () == DEV_PTYS_MAJOR)
{
fhandler_pty_slave *ptys = (fhandler_pty_slave *) fh;
if (ptys->getPseudoConsole ())
{
HANDLE h_cyg = ptys->get_output_handle_cyg ();
if (buf[len-1] == '\n' && len < NT_MAX_PATH - 1)
{
buf[len-1] = '\r';
buf[len] = '\n';
len ++;
}
WriteFile (h_cyg, buf, len, &done, 0);
FlushFileBuffers (h_cyg);
}
}
#endif
}
#ifndef NOSTRACE
if (active ())
write (category, buf, len);
#endif
strace_buf_guard.release ();
SetLastError (err);
}
void
strace::prntf (unsigned category, const char *func, const char *fmt, ...)
{
va_list ap;
va_start (ap, fmt);
vprntf (category, func, fmt, ap);
va_end (ap);
}
extern "C" void
strace_printf (unsigned category, const char *func, const char *fmt, ...)
{
va_list ap;
if ((category & _STRACE_SYSTEM) || strace.active ())
{
va_start (ap, fmt);
strace.vprntf (category, func, fmt, ap);
va_end (ap);
}
}
static NO_COPY struct tab
{
int v;
const char *n;
}
ta[] =
{
{ WM_NULL, "WM_NULL" },
{ WM_CREATE, "WM_CREATE" },
{ WM_DESTROY, "WM_DESTROY" },
{ WM_MOVE, "WM_MOVE" },
{ WM_SIZE, "WM_SIZE" },
{ WM_ACTIVATE, "WM_ACTIVATE" },
{ WM_SETFOCUS, "WM_SETFOCUS" },
{ WM_KILLFOCUS, "WM_KILLFOCUS" },
{ WM_ENABLE, "WM_ENABLE" },
{ WM_SETREDRAW, "WM_SETREDRAW" },
{ WM_SETTEXT, "WM_SETTEXT" },
{ WM_GETTEXT, "WM_GETTEXT" },
{ WM_GETTEXTLENGTH, "WM_GETTEXTLENGTH" },
{ WM_PAINT, "WM_PAINT" },
{ WM_CLOSE, "WM_CLOSE" },
{ WM_QUERYENDSESSION, "WM_QUERYENDSESSION" },
{ WM_QUIT, "WM_QUIT" },
{ WM_QUERYOPEN, "WM_QUERYOPEN" },
{ WM_ERASEBKGND, "WM_ERASEBKGND" },
{ WM_SYSCOLORCHANGE, "WM_SYSCOLORCHANGE" },
{ WM_ENDSESSION, "WM_ENDSESSION" },
{ WM_SHOWWINDOW, "WM_SHOWWINDOW" },
{ WM_WININICHANGE, "WM_WININICHANGE" },
{ WM_DEVMODECHANGE, "WM_DEVMODECHANGE" },
{ WM_ACTIVATEAPP, "WM_ACTIVATEAPP" },
{ WM_FONTCHANGE, "WM_FONTCHANGE" },
{ WM_TIMECHANGE, "WM_TIMECHANGE" },
{ WM_CANCELMODE, "WM_CANCELMODE" },
{ WM_SETCURSOR, "WM_SETCURSOR" },
{ WM_MOUSEACTIVATE, "WM_MOUSEACTIVATE" },
{ WM_CHILDACTIVATE, "WM_CHILDACTIVATE" },
{ WM_QUEUESYNC, "WM_QUEUESYNC" },
{ WM_GETMINMAXINFO, "WM_GETMINMAXINFO" },
{ WM_PAINTICON, "WM_PAINTICON" },
{ WM_ICONERASEBKGND, "WM_ICONERASEBKGND" },
{ WM_NEXTDLGCTL, "WM_NEXTDLGCTL" },
{ WM_SPOOLERSTATUS, "WM_SPOOLERSTATUS" },
{ WM_DRAWITEM, "WM_DRAWITEM" },
{ WM_MEASUREITEM, "WM_MEASUREITEM" },
{ WM_DELETEITEM, "WM_DELETEITEM" },
{ WM_VKEYTOITEM, "WM_VKEYTOITEM" },
{ WM_CHARTOITEM, "WM_CHARTOITEM" },
{ WM_SETFONT, "WM_SETFONT" },
{ WM_GETFONT, "WM_GETFONT" },
{ WM_SETHOTKEY, "WM_SETHOTKEY" },
{ WM_GETHOTKEY, "WM_GETHOTKEY" },
{ WM_QUERYDRAGICON, "WM_QUERYDRAGICON" },
{ WM_COMPAREITEM, "WM_COMPAREITEM" },
{ WM_COMPACTING, "WM_COMPACTING" },
{ WM_WINDOWPOSCHANGING, "WM_WINDOWPOSCHANGING" },
{ WM_WINDOWPOSCHANGED, "WM_WINDOWPOSCHANGED" },
{ WM_POWER, "WM_POWER" },
{ WM_COPYDATA, "WM_COPYDATA" },
{ WM_CANCELJOURNAL, "WM_CANCELJOURNAL" },
{ WM_NCCREATE, "WM_NCCREATE" },
{ WM_NCDESTROY, "WM_NCDESTROY" },
{ WM_NCCALCSIZE, "WM_NCCALCSIZE" },
{ WM_NCHITTEST, "WM_NCHITTEST" },
{ WM_NCPAINT, "WM_NCPAINT" },
{ WM_NCACTIVATE, "WM_NCACTIVATE" },
{ WM_GETDLGCODE, "WM_GETDLGCODE" },
{ WM_NCMOUSEMOVE, "WM_NCMOUSEMOVE" },
{ WM_NCLBUTTONDOWN, "WM_NCLBUTTONDOWN" },
{ WM_NCLBUTTONUP, "WM_NCLBUTTONUP" },
{ WM_NCLBUTTONDBLCLK, "WM_NCLBUTTONDBLCLK" },
{ WM_NCRBUTTONDOWN, "WM_NCRBUTTONDOWN" },
{ WM_NCRBUTTONUP, "WM_NCRBUTTONUP" },
{ WM_NCRBUTTONDBLCLK, "WM_NCRBUTTONDBLCLK" },
{ WM_NCMBUTTONDOWN, "WM_NCMBUTTONDOWN" },
{ WM_NCMBUTTONUP, "WM_NCMBUTTONUP" },
{ WM_NCMBUTTONDBLCLK, "WM_NCMBUTTONDBLCLK" },
{ WM_KEYFIRST, "WM_KEYFIRST" },
{ WM_KEYDOWN, "WM_KEYDOWN" },
{ WM_KEYUP, "WM_KEYUP" },
{ WM_CHAR, "WM_CHAR" },
{ WM_DEADCHAR, "WM_DEADCHAR" },
{ WM_SYSKEYDOWN, "WM_SYSKEYDOWN" },
{ WM_SYSKEYUP, "WM_SYSKEYUP" },
{ WM_SYSCHAR, "WM_SYSCHAR" },
{ WM_SYSDEADCHAR, "WM_SYSDEADCHAR" },
{ WM_KEYLAST, "WM_KEYLAST" },
{ WM_INITDIALOG, "WM_INITDIALOG" },
{ WM_COMMAND, "WM_COMMAND" },
{ WM_SYSCOMMAND, "WM_SYSCOMMAND" },
{ WM_TIMER, "WM_TIMER" },
{ WM_HSCROLL, "WM_HSCROLL" },
{ WM_VSCROLL, "WM_VSCROLL" },
{ WM_INITMENU, "WM_INITMENU" },
{ WM_INITMENUPOPUP, "WM_INITMENUPOPUP" },
{ WM_MENUSELECT, "WM_MENUSELECT" },
{ WM_MENUCHAR, "WM_MENUCHAR" },
{ WM_ENTERIDLE, "WM_ENTERIDLE" },
{ WM_CTLCOLORMSGBOX, "WM_CTLCOLORMSGBOX" },
{ WM_CTLCOLOREDIT, "WM_CTLCOLOREDIT" },
{ WM_CTLCOLORLISTBOX, "WM_CTLCOLORLISTBOX" },
{ WM_CTLCOLORBTN, "WM_CTLCOLORBTN" },
{ WM_CTLCOLORDLG, "WM_CTLCOLORDLG" },
{ WM_CTLCOLORSCROLLBAR, "WM_CTLCOLORSCROLLBAR" },
{ WM_CTLCOLORSTATIC, "WM_CTLCOLORSTATIC" },
{ WM_MOUSEFIRST, "WM_MOUSEFIRST" },
{ WM_MOUSEMOVE, "WM_MOUSEMOVE" },
{ WM_LBUTTONDOWN, "WM_LBUTTONDOWN" },
{ WM_LBUTTONUP, "WM_LBUTTONUP" },
{ WM_LBUTTONDBLCLK, "WM_LBUTTONDBLCLK" },
{ WM_RBUTTONDOWN, "WM_RBUTTONDOWN" },
{ WM_RBUTTONUP, "WM_RBUTTONUP" },
{ WM_RBUTTONDBLCLK, "WM_RBUTTONDBLCLK" },
{ WM_MBUTTONDOWN, "WM_MBUTTONDOWN" },
{ WM_MBUTTONUP, "WM_MBUTTONUP" },
{ WM_MBUTTONDBLCLK, "WM_MBUTTONDBLCLK" },
{ WM_MOUSELAST, "WM_MOUSELAST" },
{ WM_PARENTNOTIFY, "WM_PARENTNOTIFY" },
{ WM_ENTERMENULOOP, "WM_ENTERMENULOOP" },
{ WM_EXITMENULOOP, "WM_EXITMENULOOP" },
{ WM_MDICREATE, "WM_MDICREATE" },
{ WM_MDIDESTROY, "WM_MDIDESTROY" },
{ WM_MDIACTIVATE, "WM_MDIACTIVATE" },
{ WM_MDIRESTORE, "WM_MDIRESTORE" },
{ WM_MDINEXT, "WM_MDINEXT" },
{ WM_MDIMAXIMIZE, "WM_MDIMAXIMIZE" },
{ WM_MDITILE, "WM_MDITILE" },
{ WM_MDICASCADE, "WM_MDICASCADE" },
{ WM_MDIICONARRANGE, "WM_MDIICONARRANGE" },
{ WM_MDIGETACTIVE, "WM_MDIGETACTIVE" },
{ WM_MDISETMENU, "WM_MDISETMENU" },
{ WM_DROPFILES, "WM_DROPFILES" },
{ WM_MDIREFRESHMENU, "WM_MDIREFRESHMENU" },
{ WM_CUT, "WM_CUT" },
{ WM_COPY, "WM_COPY" },
{ WM_PASTE, "WM_PASTE" },
{ WM_CLEAR, "WM_CLEAR" },
{ WM_UNDO, "WM_UNDO" },
{ WM_RENDERFORMAT, "WM_RENDERFORMAT" },
{ WM_RENDERALLFORMATS, "WM_RENDERALLFORMATS" },
{ WM_DESTROYCLIPBOARD, "WM_DESTROYCLIPBOARD" },
{ WM_DRAWCLIPBOARD, "WM_DRAWCLIPBOARD" },
{ WM_PAINTCLIPBOARD, "WM_PAINTCLIPBOARD" },
{ WM_VSCROLLCLIPBOARD, "WM_VSCROLLCLIPBOARD" },
{ WM_SIZECLIPBOARD, "WM_SIZECLIPBOARD" },
{ WM_ASKCBFORMATNAME, "WM_ASKCBFORMATNAME" },
{ WM_CHANGECBCHAIN, "WM_CHANGECBCHAIN" },
{ WM_HSCROLLCLIPBOARD, "WM_HSCROLLCLIPBOARD" },
{ WM_QUERYNEWPALETTE, "WM_QUERYNEWPALETTE" },
{ WM_PALETTEISCHANGING, "WM_PALETTEISCHANGING" },
{ WM_PALETTECHANGED, "WM_PALETTECHANGED" },
{ WM_HOTKEY, "WM_HOTKEY" },
{ WM_PENWINFIRST, "WM_PENWINFIRST" },
{ WM_PENWINLAST, "WM_PENWINLAST" },
{ WM_ASYNCIO, "ASYNCIO" },
{ 0, 0 }};
void
strace::wm (int message, int word, int lon)
{
if (active ())
{
int i;
for (i = 0; ta[i].n; i++)
{
if (ta[i].v == message)
{
prntf (_STRACE_WM, NULL, "wndproc %d %s %d %d", message, ta[i].n, word, lon);
return;
}
}
prntf (_STRACE_WM, NULL, "wndproc %d unknown %d %d", message, word, lon);
}
}
#endif /*NOSTRACE*/