home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Monster Media 1994 #1
/
monster.zip
/
monster
/
OS2
/
CENV2_19.ZIP
/
UNHANG.CMM
< prev
next >
Wrap
Text File
|
1994-03-08
|
13KB
|
383 lines
//****************************************************************
//*** UnHang.cmm - CEnvi program to monitor PM queues and kill ***
//*** ver.1 or otherwise handle session that have hung ***
//*** queues to allow the system to continue. ***
//****************************************************************
#include <OptParms.lib>
main(argc,argv)
{
ParseInputArgs(argc,argv);
if ( 1 != argc ) {
// All the args weren't parsed, and so invalid input.
// show how to use this program.
Instructions();
} else {
if ( defined(LineCount) )
system("mode 80,%d",LineCount);
if ( HighPriority )
RunAtHighPriority();
if ( Hide )
HideMyself();
UnhangForever();
}
}
#define DEFAULT_MAX_HANGTIME 60 // how many seconds, max (approximately)
// can a program hang before we kill it
#define DEFAULT_CYCLES_PER_MAX_HANGTIME 3 // break test down to this many times
// maximum hang time, more cycles gives
// each program a better chance to not
// be hung, but takes up more time
#define DEFAULT_MINIMUM_SUSPEND_FACTOR 0 // don't suspend less than this long
Instructions()
{
printf("\a\n");
printf("UnHang.cmm - Prevent the workplace shell from hanging due to a bad app.\n");
printf("\n");
printf("SYNTAX: CEnvi UnHang.cmm [Options]\n");
printf("\n");
printf("Options: /LINES=linecount - how many lines to display in the output\n");
printf(" /ONTOP - keep this screen on top of other windows\n");
printf(" /HIDE - Hide this window\n");
printf(" /LOG=FileSpec - Filename to log hangs and attempts to unhang\n");
printf(" /HIGH - Run at high priority\n");
printf(" /TIMEOUT=# - how many seconds (max) for complete test; default %d\n", DEFAULT_MAX_HANGTIME);
printf(" /CYCLES=# - how many mini-test cycles per timeout; default %d\n", DEFAULT_CYCLES_PER_MAX_HANGTIME);
printf(" /SUSPEND=# - minimum time to suspend between checks; this keeps from\n");
printf(" \"hogging\" the cpu; 0 is bad if /HIGH; default %d\n",DEFAULT_MINIMUM_SUSPEND_FACTOR);
printf(" /COMMAND=cmd - will call this command if an application seems hung,\n");
printf(" sending Process ID as first parameter and process name\n");
printf(" as second parameter. Default is to kill the app.\n");
printf("\n");
}
LineCount;
OnTop;
Hide;
LogFileName;
HighPriority;
MaxHangtime = DEFAULT_MAX_HANGTIME;
CyclesPerMaxHangtime = DEFAULT_CYCLES_PER_MAX_HANGTIME;
MinimumSuspendFactor = DEFAULT_MINIMUM_SUSPEND_FACTOR;
HangCommand;
ParseInputArgs(argc,argv)
{
if ( OptionalParameter(argc,argv,"LINES",lTemp)
&& (LineCount=atoi(lTemp)) < 1 )
printf("Invalid LINES parameter\a\n"), abort();
OnTop = OptionalParameter(argc,argv,"ONTOP");
Hide = OptionalParameter(argc,argv,"HIDE");
OptionalParameter(argc,argv,"LOG",LogFileName);
HighPriority = OptionalParameter(argc,argv,"HIGH");
if ( OptionalParameter(argc,argv,"TIMEOUT",lTemp)
&& (MaxHangtime=atoi(lTemp)) < 1 )
printf("Invalid TIMEOUT parameter\a\n"), abort();
if ( OptionalParameter(argc,argv,"CYCLES",lTemp)
&& (CyclesPerMaxHangtime=atoi(lTemp)) < 1 )
printf("Invalid CYCLES parameter\a\n"), abort();
if ( OptionalParameter(argc,argv,"SUSPEND",lTemp)
&& (MinimumSuspendFactor=atoi(lTemp)) < 0 )
printf("Invalid SUSPEND parameter\a\n"), abort();
OptionalParameter(argc,argv,"COMMAND",HangCommand);
}
/////////////////////////////////////////////////////////////////////////
#define HWND_DESKTOP 1
#define HWND_OBJECT 2
SuspendFactor;
UnhangForever()
{
// initialize suspend factor - 25 sounds like a good number to start with
SuspendFactor = max(25,MinimumSuspendFactor);
// initialize timers to know loops are run frequently enough
TimeBetweenLoops = MaxHangtime / CyclesPerMaxHangtime ;
PMShellProblemTime = 0; // PMSHELL gets treated special; we'll let it have
// a little extra hung time to allow some other
// process to hang first
PMShellTimeout = MaxHangtime * (CyclesPerMaxHangtime + 2) / CyclesPerMaxHangtime;
for( ; ; ) { // FOREVER
LoopStartTime = time();
if ( OnTop )
PutMyselfOnTop();
// build an array of all the queue from the desktop on down
QueueList = BuildQueueList();
PList = ProcessList();
// Since it does no good to kill the very first queue, then don't test it
//QueueList++;
// For each queue, see if it is hung
printf("\n\nProcess ID Queue Size Status\n");
printf("---------- ------ ----- ------ ------");
PMShellProblem = False;
for ( q = GetArraySpan(QueueList); 0 <= q; q-- ) {
hmq = QueueList[q];
if SuspendFactor suspend(SuspendFactor);
if ( GetQueueData(hmq,PList,ProcessName,ProcessID,QueueSize) ) {
printf("\n%-10.10s %-5d %04X %-6d ",ProcessName,ProcessID,hmq,QueueSize);
if ( HungQueue(hmq) ) {
printf("HUNG");
// If this is PMSHELL, then give it a little extra time
if ( !strcmpi("PMSHELL",ProcessName) ) {
PMShellProblem = True;
if ( 0 == PMShellProblemTime )
PMShellProblemTime = time();
if ( difftime(time(),PMShellProblemTime) < PMShellTimeout )
continue;
}
LogFile("Process %s, id = %d, queue = %04X, size = %d, hung\n",
ProcessName,ProcessID,hmq,QueueSize);
AttendToHungProcess(ProcessID,ProcessName);
printf(" AND KILLED!\a\a\a");
} else {
printf("OK");
}
}
}
if ( !PMShellProblem )
PMShellProblemTime = 0;
// Adjust SuspendFactor to try to even-out processor time used by UNHANG.
// If extra time is available then increase SuspendFactor. If not
// enough time is available then decrease it.
ElapsedTestTime = difftime(time(),LoopStartTime);
ExcessTime = TimeBetweenLoops - ElapsedTestTime;
SuspendFactor = max(MinimumSuspendFactor,SuspendFactor + ExcessTime/CyclesPerMaxHangtime);
if ( 0 < ExcessTime ) {
// a little time left over, so wait until time is up
while ( difftime(time(),LoopStartTime) < TimeBetweenLoops )
suspend(SuspendFactor);
}
printf("\n New SuspendFactor = %d (from excess %d)\n",SuspendFactor,ExcessTime);
}
}
LogFile(Format)
{
if ( defined(LogFileName) ) {
if ( fp = fopen(LogFileName,"a") ) {
fprintf(fp,"\n");
fprintf(fp,ctime(time()));
va_start(valist,Format);
vfprintf(fp,Format,valist);
va_end(valist);
fclose(fp);
}
}
}
BuildQueueList() // build array of all desktop window handles
{
AddQueueForWindowAndChildren(QList,HWND_DESKTOP);
//AddQueueForWindowAndChildren(QList,HWND_OBJECT);
return(QList);
}
AddQueueForWindowAndChildren(QList,ParentHandle)
{
// Get the queue for this parent handle
Queue = GetWindowQueue(ParentHandle);
// If this queue is not already in the queue list, then add it
if ( !defined(QList) ) {
QList[0] = Queue;
} else {
for ( i = GetArraySpan(QList); 0 <= i; i-- ) {
if ( Queue == QList[i] )
break;
}
if ( i < 0 )
QList[1+GetArraySpan(QList)] = Queue;
}
// recursively call this routine for all its children
EnumHandle = WinBeginEnumWindows(ParentHandle);
while ( NULL != (WinHandle = WinGetNextWindow(EnumHandle)) ) {
if SuspendFactor suspend(SuspendFactor);
AddQueueForWindowAndChildren(QList,WinHandle);
}
WinEndEnumWindows(EnumHandle);
if SuspendFactor suspend(SuspendFactor);
}
HungQueue(hmq) // return True if this queue is hung
{
if ( !GetQueueInfo(hmq,MsgCount,ProcessID) )
// this no longer appears to be a queue; and so never mind
return(False);
// determine if queue is hung by sending lots of NULL message to it
// then wait a second and if sending null message fails then it is hung
MsgCountPerLoop = MsgCount / CyclesPerMaxHangtime ;
for ( i = MsgCountPerLoop; 0 < i--; ) {
if SuspendFactor suspend(SuspendFactor);
if ( !PostNullMsgToQueue(hmq) ) {
// there appears to have been an error posting a null message.
// and so the queue must be full (or no longer valid). Wait
// some seconds and give it one more chance
suspend( 5 * 1000 );
return( !PostNullMsgToQueue(hmq)
&& GetQueueInfo(hmq,MsgCount,ProcessID) );
}
}
return(False);
}
WinBeginEnumWindows(pHwndParent)
{
#define ORD_WIN32BEGINENUMWINDOWS 702
return DynamicLink("PMWIN",ORD_WIN32BEGINENUMWINDOWS,BIT32,CDECL,pHwndParent)
}
WinGetNextWindow(pHEnum)
{
#define ORD_WIN32GETNEXTWINDOW 756
return DynamicLink("PMWIN",ORD_WIN32GETNEXTWINDOW,BIT32,CDECL,pHEnum)
}
WinEndEnumWindows(pHEnum)
{
#define ORD_WIN32ENDENUMWINDOWS 737
return DynamicLink("PMWIN",ORD_WIN32ENDENUMWINDOWS,BIT32,CDECL,pHEnum)
}
GetWindowQueue(hwnd)
{
#define ORD_WIN32QUERYWINDOWULONG 843
#define QWL_HMQ (-4)
return DynamicLink("PMWIN",ORD_WIN32QUERYWINDOWULONG,BIT32,CDECL,
hwnd,QWL_HMQ)
}
GetQueueInfo(hmq,count,pid)
{
#define ORD_WIN32QUERYQUEUEINFO 824
BLOBSize(qinfo,4 * 5);
success = DynamicLink("PMWIN",ORD_WIN32QUERYQUEUEINFO,BIT32,CDECL,
hmq,qinfo,BLObSize(qinfo));
if ( success ) {
pid = BLObGet(qinfo,4 * 1,UWORD32);
count = BLObGet(qinfo,4 * 3,UWORD32);
}
return(success);
}
GetQueueData(hmq,PList,ProcessName,ProcessID,QueueSize)
// return data about this queue and who owns it; False if cannot
{
if ( !GetQueueInfo(hmq,QueueSize,ProcessID) )
return(False);
// to find process name, match processID to id in PList
for ( p = GetArraySpan(PList); 0 <= p; p-- ) {
if ( ProcessID == PList[p].id ) {
ProcessName = SplitFileName(PList[p].name).name;
return(True);
}
}
// if got here, then all OK except for process name
ProcessName = "?????";
return(True);
}
PostNullMsgToQueue(hmq)
{
#define ORD_WIN32POSTQUEUEMSG 902
#define WM_NULL 0
return DynamicLink("PMWIN",ORD_WIN32POSTQUEUEMSG,BIT32,CDECL,
hmq,WM_NULL,MessageID,0,0)
}
DeadList; // keep a list of processes already killed
AttendToHungProcess(id,name)
{
// determine if this process already (attempted) killed
if ( !defined(DeadList) ) {
DeadList[0] = id;
} else {
for ( _d = GetArraySpan(DeadList); 0 <= _d; _d-- ) {
if ( DeadList[_d] == id ) {
AttendToHungProcessParent(id);
}
}
if ( _d < 0 ) {
// this id hasn't been killed before; add to list
DeadList[GetArraySpan(DeadList)+1] = id;
}
}
if ( !defined(HangCommand) ) {
#define DKP_PROCESSTREE 0 // kill process and all descendents, if created by this process
#define DKP_PROCESS 1 // kill any process even if not created by this process
#define ORD_DOS32KILLPROCESS 235
DynamicLink("doscalls",ORD_DOS32KILLPROCESS,BIT32,CDECL,DKP_PROCESS,id);
} else {
system("%s %d %s",HangCommand,id,name);
}
}
AttendToHungProcessParent(id)
{
// get parent of this id, and then KILL IT
list = ProcessList();
// find entry for this process ID
for ( proc = GetArraySpan(list); 0 <= proc; proc-- ) {
if ( list[proc].id == id ) {
parent = list[proc].parent;
for ( _i = GetArraySpan(list); 0 <= _i; _i-- ) {
if ( list[_i].id == parent ) {
LogFile("Kill Parent Process %s, id = %d\n",
list[_i].name,parent);
AttendToHungProcess(parent,list[_i].name);
}
}
}
}
}
RunAtHighPriority() // make this a high priority process
{
#define ORD_DOS32SETPRIORITY 236
#define PRTYC_TIMECRITICAL 3
DynamicLink("doscalls",ORD_DOS32SETPRIORITY,BIT32,CDECL,0,PRTYC_TIMECRITICAL,0,0)
}
HideMyself()
{
#define SWP_HIDE 0x0010
#define ORD_WIN32SETWINDOWPOS 875
DynamicLink("PMWIN",ORD_WIN32SETWINDOWPOS,BIT32,CDECL,
Info().WinHandle,0,0,0,0,0,SWP_HIDE);
}
PutMyselfOnTop()
{
#define HWND_TOP 3
#define SWP_ZORDER 4
#define ORD_WIN32SETWINDOWPOS 875
DynamicLink("PMWIN",ORD_WIN32SETWINDOWPOS,BIT32,CDECL,
Info().WinHandle,HWND_TOP,0,0,0,0,SWP_ZORDER);
}