//
// pthread-specific access functions
// avoid complicated libthread_db interface
//

// Linux NPTL 2.3.2
complex list_head {
	'X'	0	next;
	'X'	4	prev;
};
complex nptl_pthread {
	'X'	0	loopback;
	'X'	0x48	tid;
};
defn isnptl() {
	return var("nptl_version") != {};
}
defn nptl2tid(p) {
	complex nptl_pthread p;
	if p.loopback != p then 
		error("bad pthread "+itoa(p, "%x"));
	return p.tid;
}
defn nptlpthreadlist() {
	local all, p, n, l;

	all = {};
	l = (list_head)stack_used;
	l = (list_head)l.next;
	while l != stack_used do {
		p = l - *_thread_db_pthread_list;
		all = append all, p;
		l = (list_head)l.next;
	}
	return all;
}

// 
// Generic dispatch
// 
defn pthreadlibrary() {
	if var("_pthreadlibrary") == {} then {
		if isnptl() then
			_pthreadlibrary = "nptl";
		else
			_pthreadlibrary = "unknown";
	}
	return _pthreadlibrary;
}

defn id2tid(id) {
	if pthreadlibrary() == "nptl" then
		return nptl2tid(id);
	error("unknown pthread library: "+pthreadlibrary);
}

defn pthreadlist() {
	if pthreadlibrary() == "nptl" then
		return nptlpthreadlist(id);
	error("unknown pthread library: "+pthreadlibrary);
}

// pick apart system mcontext_t structures
defn mcontext(m)
{
	complex mcontext_t m;

	if systype == "linux" then {
		m = m\X;
		return {"PC", m[14], "SP", m[7], "BP", m[6]};
	} else if systype == "freebsd" then {
		return {"PC", m.mc_eip, "SP", m.mc_esp, "BP", m.mc_ebp};
	} else
		error("do not know how to read mcontext_t on system "+systype);
}

//
// plan 9 thread library support
// 
defn context(c)
{
	c = (Context)c;
	return mcontext(c.uc.uc_mcontext);
}

defn contextstk(c)
{
	_stk(context(c), 0);
}

defn contextlstk(c)
{
	_stk(context(c), 1);
}

defn altfmt(A){
	local i, s, yes;
	complex Alt A;

	s = "alt(";
	s = s + "tag(*" + itoa(A.tag, "%x") + "=" + itoa(*A.tag, "%x") + ") ";
	i = 0;
	yes = 0;
	while A.op != CHANEND && A.op != CHANNOBLK do{
		if A.op != CHANNOP then{
			if yes then s = s + " ";
			s = s + itoa(i, "%d");
			s = s + ":";
			if A.op == CHANSND then s = s + "send";
			if A.op == CHANRCV then s = s + "recv";
			s = s + "(channel(";
			s = s + itoa(A.c, "%x");
			s = s + "))";
			yes = 1;
		}
		i = i + 1;
		A = (Alt)(A + sizeofAlt);
	}
	if A.op==CHANNOBLK then{
		if yes then s = s + " ";
		s = s + "noblock";
	}
	s = s + ")";
	return s;
}

defn alt(A){
	print(altfmt(A), "\n");
}

defn channel(C) {
	complex Channel C;
	local i, p;

	print("channel ", C\X, " // ", *(C.name\s));
	if C.freed then {
		print(" (moribund)");
	}
	print("\n");
	print("\telemsize=", C.elemsize\D, " bufsize=", C.bufsize, "\n");
	if C.bufsize then {
		print("\t", C.nbuf\D, " values in channel:\n");
		print("\t");
		p = C.buf+C.off*C.elemsize;
		loop 1,C.nbuf do {
			if C.elemsize==4 then {
				print(*p\X, " ");
			}else {
				print("data(", p\X, ") ");
			}
			p = p+C.elemsize;
			if p == C.buf+C.bufsize*C.elemsize then {
				p = C.buf;
			}
		}
	}
	print("\n");
	print(" senders:\n");
	_altarray(C.asend);
	print(" recvers:\n");
	_altarray(C.arecv);
}

defn _altarray(aa)
{
	local i, a, t;

	i = 0;
	aa = (_Altarray)aa;
	while i < aa.n do {
		a = (Alt)aa.a[i];
		print("\t"+threadstkline(a.thread)+"\n");
		i++;
	}
}

defn fnname(a){
	local sym, s;

	s = symbols;
	while s do {
		sym = head s;
		if sym[2] == a then
			return sym[0];
		s = tail s;
	}
	return itoa(a, "%x");
}

stkignorelist = {};
defn stkignore(s){
	append stkignorelist, s;
}

defn threadstkline(T){
	local stk, frame, pc, pc0, file, s, sym, i, stop, P, mainpid;

	T = (_Thread)T;
	P = (Proc)T.proc;
	if P.thread == T then {
		mainpid = pid;
		setproc(id2tid(P.osprocid));
		stk = strace({});
		setproc(mainpid);
	} else
		stk = strace(context(T.context));

	stop = 0;
	while stk && !stop do {
		frame = head stk;
		stk = tail stk;
		pc = frame[2];
		pc0 = frame[0];
		file = pcfile(pc);
		if !regexp("plan9/src/lib9/", file)
		&& !regexp("plan9/src/libthread/", file) 
		&& file != "?file?"
		&& match(file, stkignore)==-1 then
			stop = 1;
	}
	file = pcfile(pc);
	s = file+":"+itoa(pcline(pc), "%d");
	if pc0 != 0 then 
		s = s + " "+fnname(pc0);
	return s;
}

defn threadfmt(T){
	complex _Thread T;
	local P, s, name;

	P = (Proc)T.proc;
	s = "t=(_Thread)"+itoa(T, "%-10x")+" // ";

	if P.thread == T then
		s = s + "Running    ";
	else
		s = s + "Sleeping   ";
	s = s + threadstkline(T);

	name = T+392;	// T+offsetof(_Thread, name);
	if *(name\b) != 0 then
		s = s + " ["+*(name\s)+"]";
	return s;
}

defn thread(T){
	print(threadfmt(T), "\n");
}

defn procthreads(P){
	complex Proc P;
	local T;

	T = (_Thread)P.allthreads.$head;
	while T != 0 do{
		print("\t");
		thread(T);
		T = (_Thread)T.allnext;
	}
}

defn prociter(x) {
	local P;

	P = (Proc)*_threadprocs;
	while P != 0 do{
		if P != (Proc)*_threadprocs then print("\n");
		proc(P);
		if x == 1 then 
			procthreads(P);
		if x == 2 then
			threadstks(P);
		P = (Proc)P.next;
	}
}

defn procs() {
	prociter(0);
}

defn threads() {
	prociter(1);
}

defn stacks() {
	prociter(2);
}

threadstkignore = {
	"plan9/src/libthread/",
	"plan9/src/lib9/",
	"plan9/src/lib9/(fmt|utf)/",
};
defn threadstks(P){
	complex Proc P;
	local T,  mainpid, pref, ign;

	// mainpid = pid;
	pref = stkprefix;
	stkprefix = pref+"\t\t";
	ign = stkignore;
	stkignore = threadstkignore;
	// setproc(P.pid);
	T = (_Thread)P.allthreads.$head;
	while T != 0 do{
		print("\t");
		thread(T);
		threadstk(T);
		T = (_Thread)T.allnext;
		print("\n");
	}
	// setproc(mainpid);
	stkprefix = pref;
	stkignore = ign;
}

defn proc(P){
	complex Proc P;

	print("p=(Proc)", itoa(P, "%-10x"), "  // pthread ", P.osprocid\X, " pid ", id2tid(P.osprocid)\D, " ");
	if P.thread==0 then
		print(" Sched");
	else
		print(" Running");
	print("\n");
}

defn threadlstk(T){
	complex _Thread T;
	local P, mainpid;

	P = (Proc)T.proc;
	mainpid = pid;
	setproc(id2tid(P.osprocid));

	if P.thread == T then
		lstk();
	else
		contextlstk(T.context);
	setproc(mainpid);
}

defn threadstk(T){
	complex _Thread T;
	local P, mainpid;

	P = (Proc)T.proc;
	mainpid = pid;
	setproc(id2tid(P.osprocid));

	if P.thread == T then
		stk();
	else 
		contextstk(T.context);

	setproc(mainpid);
}

print(acidfile);
