/***********************************************************************
*                                                                      *
*               This software is part of the ast package               *
*          Copyright (c) 1985-2011 AT&T Intellectual Property          *
*          Copyright (c) 2020-2023 Contributors to ksh 93u+m           *
*                      and is licensed under the                       *
*                 Eclipse Public License, Version 2.0                  *
*                                                                      *
*                A copy of the License is available at                 *
*      https://www.eclipse.org/org/documents/epl-2.0/EPL-2.0.html      *
*         (with md5 checksum 84283fa8859daf213bdda5a9f8d1be1d)         *
*                                                                      *
*                 Glenn Fowler <gsf@research.att.com>                  *
*                  David Korn <dgk@research.att.com>                   *
*                   Phong Vo <kpv@research.att.com>                    *
*                  Martijn Dekker <martijn@inlv.org>                   *
*            Johnothan King <johnothanking@protonmail.com>             *
*                                                                      *
***********************************************************************/

#include	"vmhdr.h"

/*	Turn on tracing for regions
**
**	Written by Kiem-Phong Vo, kpv@research.att.com, 01/16/94.
*/

static int	Trfile = -1;
static char	Trbuf[128];

static char* trstrcpy(char* to, const char* from, int endc)
{	int	n;

	n = strlen(from);
	memcpy(to,from,n);
	to += n;
	if((*to = endc) )
		to += 1;
	return to;
}

/* convert a long value to an ASCII representation */
static char* tritoa(Vmulong_t	v,	/* value to convert					*/
		    int		type)	/* =0 base-16, >0: unsigned base-10, <0: signed base-10	*/
{
	char*	s;

	s = &Trbuf[sizeof(Trbuf) - 1];
	*s-- = '\0';

	if(type == 0)		/* base-16 */
	{	char*	digit = "0123456789abcdef";
		do
		{	*s-- = digit[v&0xf];
			v >>= 4;
		} while(v);
	}
	else if(type > 0)	/* unsigned base-10 */
	{	do
		{	*s-- = (char)('0' + (v%10));
			v /= 10;
		} while(v);
	}
	else			/* signed base-10 */
	{	int	sign = ((long)v < 0);
		if(sign)
			v = (Vmulong_t)(-((long)v));
		do
		{	*s-- = (char)('0' + (v%10));
			v /= 10;
		} while(v);
		if(sign)
			*s-- = '-';
	}

	return s+1;
}

/* generate a trace of some call */
static void trtrace(Vmalloc_t*	vm,		/* region call was made from	*/
		    Vmuchar_t*	oldaddr,	/* old data address		*/
		    Vmuchar_t*	newaddr,	/* new data address		*/
		    size_t	size,		/* size of piece		*/
		    size_t	align)		/* alignment			*/
{
	char		buf[1024], *bufp, *endbuf;
	Vmdata_t*	vd = vm->data;
	const char*	file = 0;
	int		line = 0;
	const char*	func = 0;
	int		comma;
	int		n;
	int		m;
	
	int		type;
#define SLOP	64

	if(oldaddr == (Vmuchar_t*)(-1)) /* printing busy blocks */
	{	type = 0;
		oldaddr = NULL;
	}
	else
	{	type = vd->mode&VM_METHODS;
		VMFLF(vm,file,line,func);
	}

	if(Trfile < 0)
		return;

	bufp = buf; endbuf = buf+sizeof(buf);
	bufp = trstrcpy(bufp, tritoa(oldaddr ? VLONG(oldaddr) : 0L, 0), ':');
	bufp = trstrcpy(bufp, tritoa(newaddr ? VLONG(newaddr) : 0L, 0), ':');
	bufp = trstrcpy(bufp, tritoa((Vmulong_t)size, 1), ':');
	bufp = trstrcpy(bufp, tritoa((Vmulong_t)align, 1), ':');
	bufp = trstrcpy(bufp, tritoa(VLONG(vm), 0), ':');
	if(type&VM_MTBEST)
		bufp = trstrcpy(bufp, "b", ':');
	else if(type&VM_MTLAST)
		bufp = trstrcpy(bufp, "l", ':');
	else if(type&VM_MTPOOL)
		bufp = trstrcpy(bufp, "p", ':');
	else if(type&VM_MTPROFILE)
		bufp = trstrcpy(bufp, "s", ':');
	else if(type&VM_MTDEBUG)
		bufp = trstrcpy(bufp, "d", ':');
	else	bufp = trstrcpy(bufp, "u", ':');

	comma = 0;
	if(file && file[0] && line > 0)
	{	if((bufp + strlen(file) + SLOP) >= endbuf)
		{	char*	f;
			for(f = bufp + strlen(file); f > file; --f)
				if(f[-1] == '/' || f[-1] == '\\')
					break; 
			file = f;
		}

		bufp = trstrcpy(bufp, "file", '=');
		n = endbuf - bufp - SLOP - 3;
		m = strlen(file);
		if(m > n)
		{	file += (m - n);
			bufp = trstrcpy(bufp, "..", '.');
		}
		bufp = trstrcpy(bufp, file, ',');
		bufp = trstrcpy(bufp, "line", '=');
		bufp = trstrcpy(bufp, tritoa((Vmulong_t)line,1), 0);
		comma = 1;
	}
	if(func)
	{	if(comma)
			*bufp++ = ',';
		bufp = trstrcpy(bufp, "func", '=');
#if 1
		bufp = trstrcpy(bufp, (const char*)func, 0);
#else
		bufp = trstrcpy(bufp, tritoa((Vmulong_t)func,0), 0);
#endif
		comma = 1;
	}
	if(comma)
		*bufp++ = ':';

	*bufp++ = '\n';
	*bufp = '\0';

	write(Trfile,buf,(bufp-buf));
}

void _vmmessage(const char* s1, long n1, const char* s2, long n2)
{
	char		buf[1024], *bufp;

	bufp = buf;
	bufp = trstrcpy(bufp, "vmalloc", ':');
	if (s1)
	{
		bufp = trstrcpy(bufp, s1, ':');
		if (n1)
			bufp = trstrcpy(bufp, tritoa(n1, 1), ':');
	}
	if (s2)
	{
		bufp = trstrcpy(bufp, s2, ':');
		if (n2)
			bufp = trstrcpy(bufp, tritoa(n2, 0), ':');
	}

	bufp = trstrcpy(bufp, tritoa((long)getpid(), 1), ':');

	*bufp++ = '\n';
	write(2,buf,(bufp-buf));
}

int vmtrace(int file)
{
	int	fd;

	_Vmstrcpy = trstrcpy;
	_Vmitoa = tritoa;
	_Vmtrace = trtrace;

	fd = Trfile;
	Trfile = file;
	return fd;
}

int vmtrbusy(Vmalloc_t* vm)
{
	Seg_t*		seg;
	Vmdata_t*	vd = vm->data;

	if(Trfile < 0 || !(vd->mode&(VM_MTBEST|VM_MTDEBUG|VM_MTPROFILE)))
		return -1;

	for(seg = vd->seg; seg; seg = seg->next)
	{	Block_t		*b, *endb;
		Vmuchar_t*	data;
		size_t		s;

		for(b = SEGBLOCK(seg), endb = BLOCK(seg->baddr); b < endb; )
		{	if(ISJUNK(SIZE(b)) || !ISBUSY(SIZE(b)))
				continue;

			data = DATA(b);
			if(vd->mode&VM_MTDEBUG)
			{	data = DB2DEBUG(data);
				s = DBSIZE(data);
			}
			else if(vd->mode&VM_MTPROFILE)
				s = PFSIZE(data);
			else	s = SIZE(b)&~BITS;

			trtrace(vm, (Vmuchar_t*)(-1), data, s, 0);

			b = (Block_t*)((Vmuchar_t*)DATA(b) + (SIZE(b)&~BITS) );
		}
	}

	return 0;
}
