Milán Major

netscape-revival

1 branch
Code

nspr/src/prdump.c

#include "prdump.h"
#include <stdio.h>
#include <stddef.h>
#include <time.h>
#include "prglobal.h"
#include "prmacros.h"
#include "prprf.h"
#ifndef XP_MAC
#include <prfile.h>
#endif
#ifdef SW_THREADS
#include "swkern.h"
#endif
#ifdef HW_THREADS
#include "hwkern.h"
#endif

static char *null_str = "<null>";

#define FIX_NULL(_p) ((_p) ? (_p) : null_str)

#if defined(XP_PC) && defined(_WIN32)

#include "prprf.h"
/*
** For the PC, _PR_DebugPrint writes to stdout which is caught by NSPR and
** redirected to an output window...
*/
int
_PR_DebugPrint(FILE *f, const char *fmt, ...)
{
    char buffer[512];
    int ret = 0;

    va_list args;
    va_start(args, fmt);
    PR_vsnprintf(buffer, sizeof(buffer), fmt, args);
    if( f == stderr ) {
        ret = PR_WRITE(1, buffer, strlen(buffer));
    } else {
        ret = fprintf(f, "%s", buffer);
    }
    va_end(args);
    return ret;
}
#endif  /* XP_PC && _WIN32 */

PR_PUBLIC_API(size_t)
PR_PrintMicrosecondsToBuf(char* buf, size_t buflen, int64 v)
{
    size_t count = 0;

#define TRY_TIME(lower, upper, units)			   \
    {							   \
	int64 lb, ub, tmp;				   \
	LL_I2L(lb, lower);				   \
	LL_DIV(tmp, v, lb);	/* divide down */	   \
	v = tmp;					   \
	LL_I2L(ub, upper);				   \
	if (LL_CMP(v, <, ub)) {				   \
	    int i;					   \
	    LL_L2I(i, v);				   \
	    count += PR_snprintf(&buf[count], buflen-count,\
				 "%d" units " ", i);	   \
	    return count;				   \
	}						   \
    }							   \

    TRY_TIME(   1, 1000, "us");
    TRY_TIME(1000, 1000, "ms");
    TRY_TIME(1000,   60, "sec");
    TRY_TIME(  60,   60, "min");
    TRY_TIME(  60,   24, "hrs");
    TRY_TIME(  24,  365, "days");
    TRY_TIME( 365, 1000, "yrs");

#undef TRY_TIME
    count += PR_snprintf(&buf[count], buflen-count, "inf ");
    return count;
}

PR_PUBLIC_API(void)
PR_PrintMicroseconds(FILE* file, int64 v)
{
    char buf[256];
    PR_PrintMicrosecondsToBuf(buf, sizeof(buf), v);
    _PR_DebugPrint(file, buf);
}

static void
pr_DumpOneThread(FILE* out, PRThread *t)
{
    int in = _PR_IntsOff();
#ifdef SW_THREADS
    _PR_DebugPrint(out, "  %-20s %8lx %3d ", FIX_NULL(t->name), (long)t, (int)t->priority);
#else
    _PR_DebugPrint(out, "  %-20s %8x %3d ", FIX_NULL(t->name), t->id, t->priority);
#endif
    switch (t->state) {
      case _PR_UNBORN:
	_PR_DebugPrint(out, "unborn");
	break;
      case _PR_RUNNING:
	_PR_DebugPrint(out, "running");
	break;
      case _PR_RUNNABLE:
	_PR_DebugPrint(out, "runnable");
	break;
      case _PR_SUSPENDED:
	_PR_DebugPrint(out, "suspended");
	break;
      case _PR_SLEEPING:
	_PR_DebugPrint(out, "sleeping ");
	PR_PrintMicroseconds(out, t->sleep);
	break;
      case _PR_COND_WAIT:
	_PR_DebugPrint(out, "condwait ");
	PR_PrintMicroseconds(out, t->sleep);
	PR_Monitor_print(out, t->monitor);
	break;
      case _PR_MON_WAIT:
	_PR_DebugPrint(out, "monwait  ");
	PR_Monitor_print(out, t->monitor);
	break;
      case _PR_ZOMBIE:
	_PR_DebugPrint(out, "zombie");
	break;
    }
    _PR_DebugPrint(out, "\n");
    if (t->dump) {
	(*t->dump)(t, out);
    }
    _PR_IntsOn(in, PR_FALSE);
}

PR_PUBLIC_API(void)
PR_DumpOneThread(PRThread *t)
{
    pr_DumpOneThread(stderr, t);
}

/* XXX use enumerate threads */
static void
pr_DumpThreads(FILE* out)
{
    PRCList *qp;
    int j;
    int in = _PR_IntsOff();

    _PR_DebugPrint(out, "\n--Thread---------------ID-------Pri-State--------------------------------------\n");
    if (PR_CurrentThread()) {
	_PR_DebugPrint(out, "Current:\n");
	pr_DumpOneThread(out, PR_CurrentThread());
    } else {
	_PR_DebugPrint(out, "Current: none\n");
    }

    _PR_DebugPrint(out, "RunQ:\n");
    for (j = 0; j < PR_NUM_PRIORITIES; j++) {
	qp = _pr_runq[j].next;
	while (qp != &_pr_runq[j]) {
	    pr_DumpOneThread(out, THREAD_PTR(qp));
	    qp = qp->next;
	}
    }
#ifdef SW_THREADS
    _PR_DebugPrint(out, "MonitorQ:\n");
    qp = _pr_monitorq.next;
    while (qp != &_pr_monitorq) {
	pr_DumpOneThread(out, THREAD_PTR(qp));
	qp = qp->next;
    }
    _PR_DebugPrint(out, "SleepQ:\n");
    qp = _pr_sleepq.next;
    while (qp != &_pr_sleepq) {
	pr_DumpOneThread(out, THREAD_PTR(qp));
	qp = qp->next;
    }
    _PR_DebugPrint(out, "SuspendQ:\n");
    qp = _pr_suspendq.next;
    while (qp != &_pr_suspendq) {
	pr_DumpOneThread(out, THREAD_PTR(qp));
	qp = qp->next;
    }
#endif
    _PR_DebugPrint(out, "-------------------------------------------------------------------------------\n");

    _PR_IntsOn(in, PR_FALSE);
}

PR_PUBLIC_API(void)
PR_DumpThreads(void)
{
    pr_DumpThreads(stderr);
}

static int
PR_Monitor_isActive(PRMonitor* mon)
{
    return (mon != NULL
	    && mon->owner != NULL
	    && mon->count != 0
	    && !PR_CLIST_IS_EMPTY(&mon->links));
}

static void
pr_DumpMonitors(FILE* out)
{
    PRCList *qp;
    int in = _PR_IntsOff();
 
    _PR_DebugPrint(out, "\n--Monitors---------------------------------------------------------------------");
    qp = _pr_all_monitors.next;
    while (qp != &_pr_all_monitors) {
	PRMonitor* mon = MONITOR_ALL_PTR(qp);
	if (PR_Monitor_isActive(mon)) {
	    _PR_DebugPrint(out, "\n  ");
	    PR_Monitor_print(out, mon);
	}
	qp = qp->next;
    }
    _PR_DebugPrint(out, "\n-------------------------------------------------------------------------------\n");

    _PR_IntsOn(in, PR_FALSE);
}

PR_PUBLIC_API(void)
PR_DumpMonitors(void)
{
    pr_DumpMonitors(stderr);
}

#undef abort

/* in prmcache.c */
extern void pr_DumpMonitorCache(FILE* out);

static void
pr_DumpStuff(FILE* out, PRBool detailed)
{
    pr_DumpThreads(out);
    pr_DumpMonitors(out);
    pr_DumpMonitorCache(out);
}

PR_PUBLIC_API(void)
PR_DumpStuff()
{
    pr_DumpStuff(stderr, PR_FALSE);
#if !defined(XP_UNIX) && !defined(XP_PC)
    abort();
#endif
}

/******************************************************************************/

PR_PUBLIC_API(void)
PR_Monitor_print(FILE* file, PRMonitor* mon)
{
    int in = _PR_IntsOff();
    if (PR_Monitor_isActive(mon))
	_PR_DebugPrint(file, "[Monitor %s count=%d flags=%x owner=%s %lx]",
		   FIX_NULL(mon->name), mon->count, mon->flags,
		   (mon->owner ? FIX_NULL(mon->owner->name) : "<none>"), (long)mon);
    else
	_PR_DebugPrint(file, "[Monitor %s %lx]", FIX_NULL(mon->name), (long)mon);
    _PR_IntsOn(in, PR_FALSE);
}

PR_PUBLIC_API(void)
PR_Thread_print(FILE* file, PRThread* t)
{
    int in = _PR_IntsOff();
    if (!t)
	_PR_DebugPrint(file, "[Thread 0]");
    else {
	_PR_DebugPrint(file, "[Thread %s pri=%d flags=%x state=",
		FIX_NULL(t->name), (int)t->priority, t->flags);
	switch (t->state) {
	  case _PR_UNBORN:
	    _PR_DebugPrint(file, "UNBORN");
	    break;
	  case _PR_RUNNING:
	    _PR_DebugPrint(file, "RUNNING");
	    break;
	  case _PR_RUNNABLE:
	    _PR_DebugPrint(file, "RUNNABLE");
	    break;
	  case _PR_SUSPENDED:
	    _PR_DebugPrint(file, "SUSPENDED");
	    break;
	  case _PR_SLEEPING:
	    _PR_DebugPrint(file, "SLEEPING ");
	    PR_PrintMicroseconds(file, t->sleep);
	    break;
	  case _PR_COND_WAIT:
	    _PR_DebugPrint(file, "COND_WAIT ");
	    PR_PrintMicroseconds(file, t->sleep);
	    PR_Monitor_print(file, t->monitor);
	    break;
	  case _PR_MON_WAIT:
	    _PR_DebugPrint(file, "MON_WAIT ");
	    PR_Monitor_print(file, t->monitor);
	    break;
	  case _PR_ZOMBIE:
	    _PR_DebugPrint(file, "ZOMBIE");
	    break;
	  default:
	    _PR_DebugPrint(file, "?");
	}
#ifdef SW_THREADS
	_PR_DebugPrint(file, " %lx]", (long)t);
#else
	_PR_DebugPrint(file, " %x]", t->id);
#endif
    }
    _PR_IntsOn(in, PR_FALSE);
}

static void
pr_ThreadFun(FILE* out, char* funName, PRThread* thread, PRMonitor* mon)
{
    _PR_DebugPrint(out, "### %s: ", FIX_NULL(funName));
    PR_Thread_print(out, thread);
    if (mon) {
	_PR_DebugPrint(out, ", ");
	PR_Monitor_print(out, mon);
    }
    _PR_DebugPrint(out, "\n");
}

PR_PUBLIC_API(void)
PR_ThreadFun(char* funName, PRThread* thread, PRMonitor* mon)
{
    pr_ThreadFun(stderr, funName, thread, mon);
}

/******************************************************************************/

static void
_PR_DefaultShowStatusHook(void)
{
    int in = _PR_IntsOff();
    pr_DumpStuff(stderr, PR_FALSE);
    _PR_IntsOn(in, PR_FALSE);
}

void (*PR_ShowStatusHook)(void) = _PR_DefaultShowStatusHook;

/******************************************************************************/

#ifndef _WIN32
#define OutputDebugString(msg)
#endif

PR_PUBLIC_API(void)
PR_DumpToFile(char* filename, char* msg, PRFileDumper dump, PRBool detailed)
{
    FILE *out;
    OutputDebugString(msg);
    out = fopen(filename, "a");
    if (!out) {
	char buf[64];
	PR_ASSERT(strlen(filename) < sizeof(buf) - 16);
	PR_snprintf(buf, sizeof(buf), "Can't open \"%s\"\n",
		    filename);
	OutputDebugString(buf);
    }
    else
    {
	struct tm *newtime;
	time_t aclock;
	int i;

	time(&aclock);
	newtime = localtime(&aclock);
	fprintf(out, "%s on %s\n", msg, asctime(newtime));  /* Print current time */
	dump(out, detailed);
	fprintf(out, "\n\n");
	for (i = 0; i < 80; i++)
	    fprintf(out, "=");
	fprintf(out, "\n\n");
	fclose(out);
    }
    OutputDebugString(" done\n");
}

/******************************************************************************/

PR_PUBLIC_API(void)
PR_DumpStuffToFile(void)
{
    PR_DumpToFile("memory.out", "Thread Summary", pr_DumpStuff, PR_FALSE);
}

/******************************************************************************/