Example #1
0
TRACE_EXPORT int TRACE_Open( const char filespec[], TRACE_file *fp )
{
    int i, j;
    RLOG_IOStruct *pInput;

    if (filespec == NULL || fp == NULL)
	return TRACEINPUT_FAIL;

    if (strcmp(filespec, "-h") == 0)
    {
	*fp = NULL;
	return TRACEINPUT_SUCCESS;
    }

    *fp = (_trace_file*)MPL_malloc(sizeof(_trace_file));
    if (*fp == NULL)
	return TRACEINPUT_FAIL;

    (*fp)->pInput = pInput = RLOG_CreateInputStruct(filespec);
    if (pInput == NULL)
    {
	MPL_free(*fp);
	*fp = NULL;
	return TRACEINPUT_FAIL;
    }

    (*fp)->bArrowAvail = (RLOG_GetNextArrow(pInput, &(*fp)->arrow) == 0);
    if (pInput->nNumRanks > 0)
    {
	(*fp)->ppEvent = (RLOG_EVENT**)MPL_malloc(sizeof(RLOG_EVENT*) * pInput->nNumRanks);
	(*fp)->ppEventAvail = (int**)MPL_malloc(sizeof(int*) * pInput->nNumRanks);

	for (i=0; i<pInput->nNumRanks; i++)
	{
	    if (pInput->pNumEventRecursions[i] > 0)
	    {
		(*fp)->ppEvent[i] = (RLOG_EVENT*)MPL_malloc(sizeof(RLOG_EVENT) * pInput->pNumEventRecursions[i]);
		(*fp)->ppEventAvail[i] = (int*)MPL_malloc(sizeof(int) * pInput->pNumEventRecursions[i]);
	    }
	    else
	    {
		(*fp)->ppEvent[i] = NULL;
		(*fp)->ppEventAvail[i] = NULL;
	    }
	}
    }
    else
    {
	(*fp)->ppEvent = NULL;
	(*fp)->ppEventAvail = NULL;
    }
    for (j=0; j<pInput->nNumRanks; j++)
    {
	for (i=0; i<pInput->pNumEventRecursions[j]; i++)
	{
	    (*fp)->ppEventAvail[j][i] = (RLOG_GetNextEvent(pInput, j+pInput->header.nMinRank, i, &(*fp)->ppEvent[j][i]) == 0);
	}
    }
    return TRACEINPUT_SUCCESS;
}
Example #2
0
/* FIXME: Add a structured comment for the man page generate to
 * create the basic documentation on this routine, particularly 
 * since this routine only prints a subset of information by default */
int main(int argc, char *argv[])
{
    RLOG_IOStruct *pInput;
    RLOG_FILE_HEADER header;
    int num_levels;
    int num_states;
    int num_arrows;
    int num_events;
    int total_num_events = 0;
    int i, j, k;
    unsigned int nMask = 0;
    int bSummary = 1;
    RLOG_STATE state;
    RLOG_EVENT event, lastevent;
    RLOG_ARROW arrow, lastarrow;
    int bFindEvent = 0;
    double dFindTime = 0.0;
    int bValidate = 0;
    int bValidateArrows = 0;
    int bOrder = 0;
    int bJumpCheck = 0;
    double dJump = 0.0;

    /* FIXME: This should also check for the GNU-standard --help, --usage,
     * and -h options.  */
    if (argc < 2)
    {
	/* FIXME: What is the default behavior with just an rlogfile? */
	printf("printrlog rlogfile [EVENTS | STATES | ARROWS | HEADER | COMM | ALL | SUMMARY ]\n");
	printf("printrlog rlogfile find endtime\n");
	printf("printrlog rlogfile validate\n");
	printf("printrlog rlogfile order\n");
	printf("printrlog rlogfile arroworder\n");
	return -1;
    }

    if (argc > 2)
    {
	nMask = 0;
	bSummary = 0;
	for (i=2; i<argc; i++)
	{
	    if (strcmp(argv[i], "EVENTS") == 0)
		nMask |= EVENT_BIT;
	    if (strcmp(argv[i], "STATES") == 0)
		nMask |= STATE_BIT;
	    if (strcmp(argv[i], "ARROWS") == 0)
		nMask |= ARROW_BIT;
	    if (strcmp(argv[i], "HEADER") == 0)
		nMask |= HEADER_BIT;
	    if (strcmp(argv[i], "COMM") == 0)
		nMask |= COMM_BIT;
	    if (strcmp(argv[i], "ALL") == 0)
		nMask = HEADER_BIT | STATE_BIT | COMM_BIT | ARROW_BIT | EVENT_BIT;
	    if (strcmp(argv[i], "SUMMARY") == 0)
	    {
		bSummary = 1;
		nMask = 0;
	    }
	    if (strcmp(argv[i], "find") == 0)
	    {
		bFindEvent = 1;
		dFindTime = atof(argv[i+1]);
	    }
	    if (strcmp(argv[i], "validate") == 0)
	    {
		bValidate = 1;
	    }
	    if (strcmp(argv[i], "order") == 0)
	    {
		bOrder = 1;
	    }
	    if (strcmp(argv[i], "arroworder") == 0)
	    {
		bValidateArrows = 1;
	    }
	    if (strcmp(argv[i], "jump") == 0)
	    {
		bJumpCheck = 1;
		if (argv[i+1])
		    dJump = atof(argv[i+1]);
	    }
	}
    }

    pInput = RLOG_CreateInputStruct(argv[1]);
    if (pInput == NULL)
    {
	printf("Error opening '%s'\n", argv[1]);
	return -1;
    }

    if (bValidateArrows)
    {
	num_arrows = RLOG_GetNumArrows(pInput);
	if (num_arrows)
	{
	    printf("num arrows: %d\n", num_arrows);
	    RLOG_GetNextArrow(pInput, &lastarrow);
	    if (lastarrow.start_time > lastarrow.end_time)
		printf("start > end: %g > %g\n", lastarrow.start_time, lastarrow.end_time);
	    while (RLOG_GetNextArrow(pInput, &arrow) == 0)
	    {
		if (arrow.start_time > arrow.end_time)
		    printf("start > end: %g > %g\n", arrow.start_time, arrow.end_time);
		if (arrow.end_time < lastarrow.end_time)
		    printf("arrows out of order: %d < %d\n", arrow.end_time, lastarrow.end_time);
		lastarrow = arrow;
	    }
	}
	RLOG_CloseInputStruct(&pInput);
	return 0;
    }

    if (bValidate)
    {
	num_arrows = RLOG_GetNumArrows(pInput);
	if (num_arrows)
	{
	    printf("num arrows: %d\n", num_arrows);
	    RLOG_GetNextArrow(pInput, &lastarrow);
	    if (lastarrow.start_time > lastarrow.end_time)
	    {
		printf("Error, arrows endtime before starttime: %g < %g\n", lastarrow.end_time, lastarrow.start_time);
		PrintArrow(&arrow);
	    }
	    while (RLOG_GetNextArrow(pInput, &arrow) == 0)
	    {
		if (lastarrow.end_time > arrow.end_time)
		{
		    printf("Error, arrows out of order: %g > %g\n", lastarrow.end_time, arrow.end_time);
		    PrintArrow(&lastarrow);
		    PrintArrow(&arrow);
		}
		if (arrow.start_time > arrow.end_time)
		{
		    printf("Error, arrows endtime before starttime: %g < %g\n", arrow.end_time, arrow.start_time);
		    PrintArrow(&arrow);
		}
		lastarrow = arrow;
	    }
	}
	for (j=pInput->header.nMinRank; j<=pInput->header.nMaxRank; j++)
	{
	    num_levels = RLOG_GetNumEventRecursions(pInput, j);
	    for (i=0; i<num_levels; i++)
	    {
		printf("Validating events in level %d:%d\n", j, i);
		if (RLOG_GetNextEvent(pInput, j, i, &event) == 0)
		{
		    if (event.end_time < event.start_time)
		    {
			printf("Error, event endtime before starttime: %g < %g\n", event.end_time, event.start_time);
		    }
		    lastevent = event;
		    while (RLOG_GetNextEvent(pInput, j, i, &event) == 0)
		    {
			if (lastevent.start_time > event.start_time)
			{
			    printf("Error, events out of order: %g > %g\n", lastevent.start_time, event.start_time);
			    PrintEvent(&lastevent);
			    PrintEvent(&event);
			}
			else if (lastevent.end_time > event.start_time)
			{
			    printf("Error, starttime before previous endtime: %g > %g\n", lastevent.end_time, event.start_time);
			    PrintEvent(&lastevent);
			    PrintEvent(&event);
			}
			if (event.end_time < event.start_time)
			{
			    printf("Error, event endtime before starttime: %g < %g\n", event.end_time, event.start_time);
			    PrintEvent(&event);
			}
			lastevent = event;
		    }
		}
	    }
	}
	RLOG_CloseInputStruct(&pInput);
	return 0;
    }

    if (bOrder)
    {
	int count = 0;
	if (RLOG_GetNextGlobalEvent(pInput, &event) != 0)
	{
	    RLOG_CloseInputStruct(&pInput);
	    return 0;
	}
	count++;
	lastevent = event;
	PrintEvent(&event);
	while (RLOG_GetNextGlobalEvent(pInput, &event) == 0)
	{
	    if (lastevent.start_time > event.start_time)
	    {
		printf("Error, events out of order: %g > %g\n", lastevent.start_time, event.start_time);
		PrintEvent(&lastevent);
		PrintEvent(&event);
	    }
	    if (event.end_time < event.start_time)
	    {
		printf("Error, event endtime before starttime: %g < %g\n", event.end_time, event.start_time);
		PrintEvent(&event);
	    }
	    lastevent = event;
	    PrintEvent(&event);
	    count++;
	}
	RLOG_CloseInputStruct(&pInput);
	printf("%d events traversed\n", count);
	return 0;
    }

    if (bFindEvent)
    {
	for (j=pInput->header.nMinRank; j<=pInput->header.nMaxRank; j++)
	{
	    printf("rank %d\n", j);
	    num_levels = RLOG_GetNumEventRecursions(pInput, j);
	    for (i=0; i<num_levels; i++)
	    {
		RLOG_FindEventBeforeTimestamp(pInput, j, i, dFindTime, &event, &k);
		PrintEventAndIndex(&event, k);
	    }
	}
	RLOG_CloseInputStruct(&pInput);
	return 0;
    }

    if (bJumpCheck)
    {
	printf("Start:\n");
	RLOG_FindGlobalEventBeforeTimestamp(pInput, dJump, &event);
	PrintEvent(&event);
	/*RLOG_PrintGlobalState(pInput);*/
	printf("Previous 10:\n");
	for (i=0; i<10; i++)
	{
	    RLOG_GetPreviousGlobalEvent(pInput, &event);
	    PrintEvent(&event);
	}
	printf("Start:\n");
	RLOG_FindGlobalEventBeforeTimestamp(pInput, dJump, &event);
	PrintEvent(&event);
	printf("Next 10:\n");
	for (i=0; i<10; i++)
	{
	    RLOG_GetNextGlobalEvent(pInput, &event);
	    PrintEvent(&event);
	}
	return 0;
    }

    if (RLOG_GetFileHeader(pInput, &header))
    {
	printf("unable to read the file header\n");
	RLOG_CloseInputStruct(&pInput);
	return -1;
    }
    if (nMask & HEADER_BIT || bSummary)
    {
	printf("min rank: %d\n", header.nMinRank);
	printf("max rank: %d\n", header.nMaxRank);
    }

    if (nMask & STATE_BIT || bSummary)
    {
	num_states = RLOG_GetNumStates(pInput);
	if (num_states)
	{
	    printf("num states: %d\n", num_states);
	    if (nMask & STATE_BIT)
	    {
		for (i=0; i<num_states; i++)
		{
		    RLOG_GetNextState(pInput, &state);
		    PrintState(&state);
		}
	    }
	}
    }

    if (nMask & ARROW_BIT || bSummary)
    {
	num_arrows = RLOG_GetNumArrows(pInput);
	if (num_arrows)
	{
	    printf("num arrows: %d\n", num_arrows);
	    if (nMask & ARROW_BIT)
	    {
		for (i=0; i<num_arrows; i++)
		{
		    RLOG_GetNextArrow(pInput, &arrow);
		    PrintArrow(&arrow);
		}
	    }
	}
    }

    if (nMask & EVENT_BIT || bSummary)
    {
	for (k=pInput->header.nMinRank; k<=pInput->header.nMaxRank; k++)
	{
	    total_num_events = 0;
	    num_levels = RLOG_GetNumEventRecursions(pInput, k);
	    if (num_levels > 0)
	    {
		printf("rank %d\n", k);
		printf("num event recursions: %d\n", num_levels);
		for (i=0; i<num_levels; i++)
		{
		    num_events = RLOG_GetNumEvents(pInput, k, i);
		    total_num_events += num_events;
		    printf(" level %d, num events: %d\n", i, num_events);
		    if (nMask & EVENT_BIT)
		    {
			for (j=0; j<num_events; j++)
			{
			    RLOG_GetNextEvent(pInput, k, i, &event);
			    PrintEvent(&event);
			}
		    }
		}
		printf("num events total: %d\n", total_num_events);
	    }
	}
    }

    RLOG_CloseInputStruct(&pInput);

    return 0;
}
Example #3
0
TRACE_EXPORT int TRACE_Get_next_primitive( const TRACE_file fp, 
                              int *category_index, 
                              int *n_tcoords, double tcoord_base[],
                              int *tcoord_pos, const int tcoord_max, 
                              int *n_ycoords, int ycoord_base[], 
                              int *ycoord_pos, const int ycoord_max,
                              int *n_bytes, char byte_base[],
                              int *byte_pos, const int byte_max )
{
    int i, j, rank = 1, level = -1;
    double dmin;
    RLOG_BOOL done = FALSE;

    *n_bytes = 0;

    for (j=0; j<fp->pInput->nNumRanks; j++)
    {
	for (i=0; i<fp->pInput->pNumEventRecursions[j]; i++)
	{
	    if (fp->ppEventAvail[j][i])
	    {
		level = i;
		rank = j;
		dmin = fp->ppEvent[j][i].end_time;
		break;
	    }
	}
    }
    if (level == -1)
    {
	if (!fp->bArrowAvail)
	    return TRACEINPUT_FAIL;
	*category_index = RLOG_ARROW_EVENT_ID;
	if (fp->arrow.leftright == RLOG_ARROW_RIGHT)
	{
	    PackQuadDouble(
		fp->arrow.start_time, 
		fp->arrow.end_time, 
		n_tcoords, tcoord_base, tcoord_pos, tcoord_max);
	}
	else
	{
	    PackQuadDouble(
		fp->arrow.end_time,
		fp->arrow.start_time, 
		n_tcoords, tcoord_base, tcoord_pos, tcoord_max);
	}
	PackQuadInt(
	    fp->arrow.src, 
	    fp->arrow.dest, 
	    n_ycoords, ycoord_base, ycoord_pos, ycoord_max);
	fp->bArrowAvail = (RLOG_GetNextArrow(fp->pInput, &fp->arrow) == 0);
	return TRACEINPUT_SUCCESS;
    }
    for (j=0; j<fp->pInput->nNumRanks; j++)
    {
	for (i=0; i<fp->pInput->pNumEventRecursions[j]; i++)
	{
	    if (fp->ppEventAvail[j][i])
	    {
		if (fp->ppEvent[j][i].end_time < dmin)
		{
		    level = i;
		    rank = j;
		    dmin = fp->ppEvent[j][i].end_time;
		}
	    }
	}
    }
    if (fp->bArrowAvail)
    {
	if (fp->arrow.end_time < dmin)
	{
	    *category_index = RLOG_ARROW_EVENT_ID;
	    if (fp->arrow.leftright == RLOG_ARROW_RIGHT)
	    {
		PackQuadDouble(
		    fp->arrow.start_time, 
		    fp->arrow.end_time, 
		    n_tcoords, tcoord_base, tcoord_pos, tcoord_max);
	    }
	    else
	    {
		PackQuadDouble(
		    fp->arrow.end_time,
		    fp->arrow.start_time, 
		    n_tcoords, tcoord_base, tcoord_pos, tcoord_max);
	    }
	    PackQuadInt(
		fp->arrow.src, 
		fp->arrow.dest, 
		n_ycoords, ycoord_base, ycoord_pos, ycoord_max);
	    fp->bArrowAvail = (RLOG_GetNextArrow(fp->pInput, &fp->arrow) == 0);
	    return TRACEINPUT_SUCCESS;
	}
    }
    *category_index = fp->ppEvent[rank][level].event;
    PackQuadDouble(
	fp->ppEvent[rank][level].start_time, 
	fp->ppEvent[rank][level].end_time, 
	n_tcoords, tcoord_base, tcoord_pos, tcoord_max);
    PackQuadInt(
	fp->ppEvent[rank][level].rank, 
	fp->ppEvent[rank][level].rank, 
	n_ycoords, ycoord_base, ycoord_pos, ycoord_max);
    fp->ppEventAvail[rank][level] = 
	(RLOG_GetNextEvent(
			    fp->pInput, 
			    rank + fp->pInput->header.nMinRank, 
			    level, 
			    &fp->ppEvent[rank][level]) == 0);

    return TRACEINPUT_SUCCESS;
}
void CRimshotView::DrawToCanvas(CDC *pDC)
{
    CRect rect, big_rect, client_rect;
    RLOG_EVENT event;
    RLOG_ARROW arrow;
    double dx;
    int height, lip;
    CBrush *pBrush;
    int i, j, k, x, y;
    HCURSOR hOldCursor;
    double dwPixel, *pdNextPixel;
    int nMaxRecursions = 0;
    CBrush white_brush;
    CPen line_pen, cursor_pen;
    CString str;
    CSize size;
    double duration;

    CRimshotDoc* pDoc = GetDocument();
    ASSERT_VALID(pDoc);

    GetClientRect(&client_rect);
    GetClientRect(&big_rect);
    big_rect.DeflateRect(25, 25);

    line_pen.CreatePen(PS_SOLID, 1, RGB(100,100,100));
    cursor_pen.CreatePen(PS_SOLID, 1, RGB(255,255,255));

    pDC->FillSolidRect(client_rect, RGB(255,255,255));
    pDC->FillSolidRect(big_rect, RGB(0,0,0));

    if (pDoc->m_pInput)
    {
	hOldCursor = SetCursor( LoadCursor(NULL, IDC_WAIT) );

	dx = ((double)big_rect.right - (double)big_rect.left) / (pDoc->m_dRight - pDoc->m_dLeft);
	height = (big_rect.bottom - big_rect.top) / pDoc->m_pInput->nNumRanks;

	for (i=0; i<pDoc->m_pInput->nNumRanks; i++)
	    nMaxRecursions = max(pDoc->m_pInput->pNumEventRecursions[i], nMaxRecursions);
	lip = (height / 2) / (nMaxRecursions + 1);

	dwPixel = 1.0 / dx;
	pdNextPixel = new double[pDoc->m_pInput->nNumRanks];

	// draw the horizontal rank lines
	pDC->SelectObject(&line_pen);
	for (i=0; i<pDoc->m_pInput->nNumRanks; i++)
	{
	    y = big_rect.top + (height * i) + (height / 2);
	    pDC->MoveTo(big_rect.left, y);
	    pDC->LineTo(big_rect.right, y);
	}

	// draw the event boxes
	for (j=0; j<pDoc->m_pInput->nNumRanks; j++)
	{
	    for (i=0; i<pDoc->m_pInput->pNumEventRecursions[j]; i++)
	    {
		if (RLOG_FindEventBeforeTimestamp(pDoc->m_pInput, 
		    pDoc->m_pInput->header.nMinRank + j, i, pDoc->m_dLeft, &event, NULL) == 0)
		{
		    if (event.end_time < pDoc->m_dLeft)
			RLOG_GetNextEvent(pDoc->m_pInput, pDoc->m_pInput->header.nMinRank + j, i, &event);
		    else
		    {
			if (event.start_time < pDoc->m_dLeft)
			    event.start_time = pDoc->m_dLeft;
		    }
		    for (k=0; k<pDoc->m_pInput->nNumRanks; k++)
			pdNextPixel[k] = event.start_time;
		    while (event.start_time < pDoc->m_dRight)
		    {
			if (event.end_time > pdNextPixel[event.rank])
			{
			    pdNextPixel[event.rank] = event.end_time + dwPixel;
			    rect.top = big_rect.top + (event.rank * height) + (lip * (i+1));
			    rect.bottom = rect.top + height - (lip * (i+1) * 2);
			    rect.left = big_rect.left + (long)(dx * (event.start_time - pDoc->m_dLeft));
			    rect.right = rect.left + (long)(dx * (event.end_time - event.start_time));
			    if (rect.left == rect.right) rect.right++;
			    if (rect.right > big_rect.right)
				rect.right = big_rect.right;
			    
			    pDC->FillSolidRect(rect, pDoc->GetEventColor(event.event));
			}
			
			if (RLOG_GetNextEvent(pDoc->m_pInput, 
			    pDoc->m_pInput->header.nMinRank + j, i, &event) != 0)
			    event.start_time = pDoc->m_dRight + 1;
		    }
		}
	    }
	}

	// draw the arrows
	// This isn't exactly right because I don't think the arrows are stored in end time order.
	if (RLOG_FindArrowBeforeTimestamp(pDoc->m_pInput, pDoc->m_dLeft, &arrow, NULL) == 0)
	{
	    if (arrow.end_time < pDoc->m_dLeft)
		RLOG_GetNextArrow(pDoc->m_pInput, &arrow);
	    //pDC->SelectObject(&line_pen);
	    pDC->SelectObject(GetStockObject(WHITE_PEN));
	    while (arrow.end_time < pDoc->m_dRight)
	    {
		x = big_rect.left + (long)(dx * (arrow.start_time - pDoc->m_dLeft));
		y = big_rect.top + (height * arrow.src) + (height / 2);
		pDC->MoveTo(x, y);
		pDC->Ellipse(x-5, y-5, x+5, y+5);
		x = x + (long)(dx * (arrow.end_time - arrow.start_time));
		y = big_rect.top + (height * arrow.dest) + (height / 2);
		pDC->LineTo(x, y);
		if (RLOG_GetNextArrow(pDoc->m_pInput, &arrow) != 0)
		    arrow.end_time = pDoc->m_dRight + 1;
	    }
	}

	// draw the vertical cursor line
	pDC->SelectObject(&cursor_pen);
	pDC->MoveTo((big_rect.right + big_rect.left) / 2, big_rect.top);
	pDC->LineTo((big_rect.right + big_rect.left) / 2, big_rect.bottom);

	pDC->SetBkMode(OPAQUE);
	pDC->SetBkColor(RGB(255,255,255));
	pDC->SetTextColor(RGB(0,0,0));

	// draw the ranks
	for (i=0; i<pDoc->m_pInput->nNumRanks; i++)
	{
	    y = big_rect.top + (height * i) + (height / 2);
	    str.Format("%d", i);
	    size = pDC->GetTextExtent(str);
	    pDC->TextOut((big_rect.left - size.cx - 7) >= 0 ? (big_rect.left - size.cx - 7) : 0, y - (size.cy / 2), str);
	}

	// draw the box, event description and duration
	if (RLOG_GetCurrentGlobalEvent(pDoc->m_pInput, &event) == 0)
	{
	    pBrush = pDoc->GetEventBrush(event.event);
	    rect.left = (big_rect.left + big_rect.right) / 2;
	    rect.right = rect.left + 13;
	    rect.top = 7;
	    rect.bottom = 20;
	    pDC->FillRect(&rect, pBrush);

	    //str.Format("%3d: %s    duration: %.6f", event.rank, pDoc->GetEventDescription(event.event), event.end_time - event.start_time);
	    //pDC->TextOut(rect.left + 20, 7, str);
	    str.Format("%3d: %s ", event.rank, pDoc->GetEventDescription(event.event));
	    size = pDC->GetTextExtent(str);
	    pDC->TextOut(rect.left + 13, 7, str);
	    //pDC->TextOut(rect.left - size.cx, 7, str);

	    duration = event.end_time - event.start_time;
	    str.Format("%.9f", duration);
	    size = pDC->GetTextExtent(str);
	    x = rect.left - 7 - size.cx;
	    // write the milliseconds
	    str.Format("%.3f", duration);
	    size = pDC->GetTextExtent(str);
	    CRgn rgn;
	    rgn.CreateRectRgn(x, 7, x + size.cx, 7 + size.cy);
	    pDC->SelectClipRgn(&rgn, RGN_COPY);
	    str.Format("%.6f", duration);
	    pDC->SetTextColor(RGB(0,0,0));
	    pDC->TextOut(x, 7, str);
	    // write the microseconds
	    str.Format("%.6f", duration);
	    size = pDC->GetTextExtent(str);
	    rgn.CreateRectRgnIndirect(&client_rect);
	    pDC->SelectClipRgn(&rgn, RGN_XOR);
	    rgn.CreateRectRgn(x, 7, x + size.cx, 7 + size.cy);
	    str.Format("%.9f", duration);
	    pDC->SetTextColor(RGB(255,0,0));
	    pDC->TextOut(x, 7, str);
	    // write the nanoseconds
	    pDC->SelectClipRgn(&rgn, RGN_COPY);
	    rgn.CreateRectRgnIndirect(&client_rect);
	    pDC->SelectClipRgn(&rgn, RGN_XOR);
	    str.Format("%.9f", duration);
	    pDC->SetTextColor(RGB(0,0,255));
	    pDC->TextOut(x, 7, str);
	    pDC->SetTextColor(RGB(0,0,0));

	    rect.top = big_rect.top + (event.rank * height) + (lip * (event.recursion+1));
	    rect.bottom = rect.top + height - (lip * (event.recursion+1) * 2);
	    rect.left = big_rect.left + (long)(dx * (event.start_time - pDoc->m_dLeft));
	    rect.right = rect.left + (long)(dx * (event.end_time - event.start_time));
	    if (rect.left == rect.right) rect.right++;
	    if (rect.right > big_rect.right)
		rect.right = big_rect.right;
	    white_brush.CreateStockObject(WHITE_BRUSH);
	    pDC->FrameRect(&rect, &white_brush);

	    // draw the current start time
	    str.Format("%.0f", event.start_time);
	    size = pDC->GetTextExtent(str);
	    x = ((big_rect.left + big_rect.right) / 2) - size.cx;
	    y = big_rect.bottom + 7;
	    str.Format("%.3f", event.start_time);
	    size = pDC->GetTextExtent(str);
	    rgn.CreateRectRgn(x, y, x + size.cx, y + size.cy);
	    pDC->SelectClipRgn(&rgn, RGN_COPY);
	    str.Format("%.6f", event.start_time);
	    pDC->SetTextColor(RGB(0,0,0));
	    pDC->TextOut(x, y, str);
	    rgn.CreateRectRgnIndirect(&client_rect);
	    pDC->SelectClipRgn(&rgn, RGN_XOR);
	    pDC->SetTextColor(RGB(255,0,0));
	    pDC->TextOut(x, y, str);
	    pDC->SetTextColor(RGB(0,0,0));
	}
	else
	{
	    str.Format("%.6f", (pDoc->m_dLeft + pDoc->m_dRight) / 2.0);
	    size = pDC->GetTextExtent(str);
	    pDC->TextOut(((big_rect.left + big_rect.right) / 2) - (size.cx / 2), big_rect.bottom + 7, str);
	}

	// draw the left and right timestamps
	str.Format("%.6f", pDoc->m_dLeft);
	pDC->TextOut(big_rect.left, big_rect.bottom + 7, str);
	str.Format("%.6f", pDoc->m_dRight);
	size = pDC->GetTextExtent(str);
	pDC->TextOut(big_rect.right - size.cx, big_rect.bottom + 7, str);

	delete pdNextPixel;
	SetCursor(hOldCursor);
    }
}