/*static char *SCCSID = "@(#)profile.c	6.1 90/11/17";*/
/*  SCCSID = @(#)profile.c 13.5 89/12/14 */

/*  profile.c - dynlink library for OS/2 system profiling
*
*   AUTHOR:	Benjamin W. Slivka
*
*   USAGE:	See dynlink entry points
*
*   HISTORY:	05-May-1987 bws Initial version
*		11-May-1987 bws Added verbose output options
*		12-May-1987 bws Works for missing map segments
*		13-May-1987 bws Made into dynlink library
*		24-May-1987 bws Changed API to DosProfile
*		28-Jun-1989 bws Changes for 386 OS/2
*
*   WARNING:	This code must be compiled SS!=DS, and cannot use C runtime!
*
*   Dynlink Entry Points:
*	ProfInit(type,pointer);
*	ProfClear(type);
*	ProfOn(type);
*	ProfOff(type);
*	ProfDump(type,file);
*	ProfFree(type);
*
*   Format of DUMP file:
*	The DUMP file consists of 1 or more "data blocks".  The format of
*	a data block is:
*
*	    Offset  Length  Contents
*	    ------  ------  --------
*	       0      A     Module Name
*	       A      2     Segment number within module (file segment number)
*	       C      2     Size in bytes of count data following
*	       E    [...]   Table of dword counts
*
*	Block 0 has "scalar data"
*
*/

#include <stdio.h>
#include <string.h>
#include <dos.h>
#include "doscalls.h"

#include "casts.h"
#include "sysprof.h"
#include "profile.h"

#define min(a,b) ((a)<(b) ? (a) : (b))

#define     ERROR_INVALID_DATA	    13

#define     NO_SHARE_SEG    0
#define     MAX_SEG_SIZE    0
#define     MAX_DATA_SIZE   65536L-4	// size of counters per call

/*  Check return code */
#define     crc(a)  {unsigned rc; if (rc=(a)) return rc;}

word far pascal ProfClear(type)
    int type;
{
    int unused;
    return DOSPROFILE(PROF_CLEAR, type, 0, (FPC)NULL, (FPC)NULL, (FPU)&unused);
}

word far pascal ProfFree(type)
    int type;
{
    int unused;
    return DOSPROFILE(PROF_FREE , type, 0, (FPC)NULL, (FPC)NULL, (FPU)&unused);
}

word far pascal ProfInit(type,modname)
    int type;
    FPC modname;
{
    int unused;
    return DOSPROFILE(PROF_ALLOC, type, 0, (FPC)modname, (FPC)NULL, (FPU)&unused);
}

word far pascal ProfOff(type)
    int type;
{
    int unused;
    return DOSPROFILE(PROF_OFF	, type, 0, (FPC)NULL, (FPC)NULL, (FPU)&unused);
}

word far pascal ProfOn(type)
    int type;
{
    int unused;
    return DOSPROFILE(PROF_ON	, type, 0, (FPC)NULL, (FPC)NULL, (FPU)&unused);
}


word far pascal ProfDump (type,filename)
    int     type;
    FPC     filename;
{
    unsigned	hFile;		/* file handle */
    int 	i;		/* segment number */
    int 	j;		/* index for zeroing name */
    int 	more=TRUE;	/* loop exit */
    char	name[MOD_NAME_SIZE]; /* module name */
    FPC 	pData;		/* ptr to data buffer */
    unsigned	uSel;			/* BUGBUG */
    word	rc;		/* return code */
    word	segNum; 	/* file segment number */
    word	unused; 	/* unused */
    ULONG	cbData; 	// length of tick buffer data
    ULONG	cbWrite;	// amount to write
    char	ach[80];	// debug string
    int 	cb;

    rc = DOSOPEN (filename,
		(FPU)&hFile,
		(FPU)&unused,
		0L,
		0,
		0x0012,
		0x0021,
		0L);
    if (rc)
	return rc;

    rc = DOSALLOCSEG(MAX_SEG_SIZE,(FPU)&uSel,NO_SHARE_SEG);
    if (rc) {
	DOSCLOSE(hFile);
	return rc;
    }

    pData = (FPC)((unsigned long)uSel << 16);

    for (i=0; more; i++) {
	for (j=0; j<MOD_NAME_SIZE; j++)     /* fill name with NULLs */
	    name[j] = '\0';
	if (rc=DOSPROFILE(PROF_DUMP,type,i,(FPC)pData,(FPC)name,(FPU)&segNum)) {
	    if (rc != ERROR_INVALID_DATA)
		return rc;
	    more = FALSE;
	}
	else {	/* write info to file */
	    rc = DOSWRITE(hFile, (FPC)name, MOD_NAME_SIZE, (FPU)&unused);
	    if (rc) {
		DOSCLOSE(hFile);
		return rc;
	    }

	    cbData = *(FPUL)pData;

	    // write segment/object number
	    rc = DOSWRITE(hFile, (FPC)&segNum, 2, (FPU)&unused);
	    if (rc) {
		DOSCLOSE(hFile);
		return rc;
	    }

	    // write length of "segment"
	    rc = DOSWRITE(hFile, (FPC)&cbData, sizeof(ULONG), (FPU)&unused);
	    if (rc) {
		DOSCLOSE(hFile);
		return rc;
	    }

	    // write data; if size was > segment, have to get in chunks
	    while (cbData > 0) {
		cbWrite = min(cbData,MAX_DATA_SIZE);
		rc = DOSWRITE(hFile, pData+4 , cbWrite, (FPU)&unused);
		if (rc) {
			DOSCLOSE(hFile);
			return rc;
		}
		cbData -= cbWrite; // adjust count remaining to get
		if (cbData != 0) { // get next chunk
		    rc=DOSPROFILE(PROF_DUMP,type,i,(FPC)pData,(FPC)name,(FPU)&segNum);
		    if (rc)
			return rc; // unexpected failure
		}
	    }
	}
    }
    rc = DOSFREESEG(uSel);
    DOSCLOSE(hFile);
    return rc;
}
