Re: Polymon

Dmitry Kurochkin <dmitry.kurochkin-LbpN3Auhdh+Rtz+nHKFJDvXmoG9oyoYx@public.gmane.org> Fri, 20 Jan 2012 02:34:27 +0400
Newsgroups gmane.comp.web.web-polygraph.user
Message-ID <[email protected]>
Hello Michael.

I am afraid the polymon is not very well documented.  I was able to
reproduce the segfault you reported.  The attached patch should fix it
and few other problems.  Also the patch adds a basic help screen (press
'h') which would hopefully help you to get started.  More info regarding
polymon usage can be found in the mailing list archives [1].

Regards,
  Dmitry

[1] http://www.web-polygraph.org/mail-archive/users/199912/0069.html

_______________________________________________
Users mailing list
Users-mN9fGWdlm5pku/f+YMZH/di2O/[email protected]
http://www.web-polygraph.org/mailman/listinfo/users
polymon-fixes_v4.0.10.patch (text/x-diff, 23.8 KB)
polymon-fixes_v4.0.10.patch - patch for Web Polygraph v4.0.10

Various polymon improvements: few segfault fixes and basic help screen.

diff --git a/src/monitor/polymon.cc b/src/monitor/polymon.cc
index 4b70415..d349b46 100644
--- src/monitor/polymon.cc
+++ src/monitor/polymon.cc
@@ -147,73 +147,88 @@ struct HostFilter {
 
 
 // used in some "matrix" windows to remember screen content
 class WinMatrix {
 	public:
 		WinMatrix(int aMaxY, int aMaxX);
 
 		double operator ()(int y, int x) const { return theImage[safePos(y,x)]; }
 		double &operator ()(int y, int x) { return theImage[safePos(y,x)]; }
 
 	protected:
 		int safePos(int y, int x) const;
 
 	protected:
 		Array<double> theImage;
 		int theMaxY;
 		int theMaxX;
 };
 
 // WINDOW wrapper with a title and event handlers
-class Window: public WINDOW {
+class Window {
 	public:
 		typedef void (Window::*EventHandler)(const Host &);
 
 	public:
 		Window(const char *aTitle);
 		virtual ~Window();
 
 		virtual void noteAdd(const Host &) {}
 		virtual void noteDel(const Host &) {}
 		virtual void noteUpd(const Host &) {}
 
+	private:
+		// disable copying
+		Window(const Window &);
+		Window &operator =(const Window &);
+
 	public:
-		const char *theTitle;
+		WINDOW *const handle;
+		const char *title;
 };
 
 // general monitor information
 class InfoWindow: public Window {
 	public:
 		InfoWindow(const char *aTitle);
 
 		virtual void noteAdd(const Host &);
 		virtual void noteDel(const Host &);
 		virtual void noteUpd(const Host &);
 
 	protected:
 		void update();
 
 	protected:
 		int theMsgCnt;       // msgs seen so far
 		int theListnCnt;     // derived from CopyCnt in the fwded msg
 };
 
+// help window
+class HelpWindow: public Window {
+	public:
+		HelpWindow();
+
+	protected:
+		void update();
+};
+
 // summary information about SmxWindows
 class MsgSum;
 class SumWindow: public Window {
 	public:
 		SumWindow(const char *aTitle);
 
 		virtual void noteAdd(const Host &);
 		virtual void noteDel(const Host &);
 		virtual void noteUpd(const Host &);
 
 	protected:
 		void update();
 		void displaySide(int &y, int x, const MsgSum &sum);
 		void displayLine(int y, int x, const char *label, const AggrStat &stats, const char *meas, double scale = 1);
 };
 
 // single measurement matrix window
 class SmxWindow: public Window {
 	public:
 		SmxWindow(const char *aTitle);
@@ -267,41 +282,41 @@ class MsgSum {
 
 	public:
 		String theLabels;
 		AggrStat theReqRate;
 		AggrStat theRepRate;
 		AggrStat theBwidth;
 		AggrStat theRespTime;
 		AggrStat theDHR;
 		AggrStat theConnUse;
 		AggrStat theErrRatio;
 		AggrStat theXactTotCnt;
 		AggrStat theTotErrRatio;
 		AggrStat theSockInstCnt;
 };
 
 /* smx windows for all attributes */
 
 struct RunLabelSmxWin: public SmxWindow {
 	RunLabelSmxWin(const char *aTitle): SmxWindow(aTitle) {}
 	virtual void displayMsg(int y, int x, const StatusFwdMsg &msg) {
-		mvwprintw(this, y, x, (char*)"%7.6s", msg.theLabel);
+		mvwprintw(handle, y, x, (char*)"%7.6s", msg.theLabel);
 	}
 };
 
 struct RunTimeSmxWin: public SmxWindow {
 	RunTimeSmxWin(const char *aTitle): SmxWindow(aTitle) {}
 	virtual void displayMsg(int y, int x, const StatusFwdMsg &msg);
 };
 
 struct ReqRateSmxWin: public NumxWindow {
 	ReqRateSmxWin(const char *aTitle): NumxWindow(aTitle, "%7.1f") {}
 	virtual double msg2num(const StatusFwdMsg &msg) {
 		return (double)msg.theReqRate;
 	}
 };
 
 struct RepRateSmxWin: public NumxWindow {
 	RepRateSmxWin(const char *aTitle): NumxWindow(aTitle, "%7.1f") {}
 	virtual double msg2num(const StatusFwdMsg &msg) {
 		return (double)msg.theRepRate;
 	}
@@ -374,50 +389,53 @@ class MsgGapSmxWin: public SmxWindow {
 	public:
 		MsgGapSmxWin(const char *aTitle): SmxWindow(aTitle) {}
 
 		virtual void noteUpd(const Host &host);
 
 	protected:
 		virtual void displayMsg(int y, int x, const StatusFwdMsg &msg);
 
 	protected:
 		StatusFwdMsg theLastMsg;
 };
 
 
 /* globals */
 
 static NetAddr TheDisp;
 
 static FileScanner *TheScanner = 0;
 static bool DoShutdown = false;
 
-static Array<Host*> TheHosts;
+static PtrArray<Host*> TheHosts;
 static Array<Host*> TheHostIdx; // address -> host map
 static int TheBusyHostCnt = 0;
-static Array<String*> TheLabels;
+static PtrArray<String*> TheLabels;
 static int TheUniqLblCnt = 0;
 
-static Array<Window*> TheWins;
+static PtrArray<Window*> TheWins;
 static int TheWinPos = 0; // current window
 static Window *TheInfoWin = 0; // special
 
+static Window *TheHelpWin = 0; // help window
+static bool ShowingHelp = false; // true iff help window is shown
+
 static HostFilter TheFilter;
 static char TheFiltWarn[80] = "";
 static int TheSelHostCnt = 0;
 
 static const int TheXMargin = 5;
 static const int TheYMargin = 4;
 static const int TheXCellWidth = 7;
 static const int TheYCellWidth = 2;
 
 static void NoteMsg(const StatusFwdMsg &msg);
 static void Broadcast(Window::EventHandler weh, const Host &host);
 static void DeleteIdleHosts();
 static bool AddFirstLabel(const Host *skip, const String &l);
 static bool DelLastLabel(const Host *skip, const String &l);
 static void SelectHost(Host *host);
 static int SwitchWin(int idx);
 static int SwitchLblFilter(int idx);
 static int SwitchCatFilter(int idx);
 static void BuildFiltWarn(ostream &os);
 
@@ -429,53 +447,67 @@ inline int Dbl2Int(double v) { return (int)rint(v); }
 KbdMonitor::KbdMonitor(int aFD): theFD(aFD) {
 	theReserv = TheScanner->setFD(theFD, dirRead, this);
 }
 
 KbdMonitor::~KbdMonitor() {
 	if (theReserv)
 		TheScanner->clearRes(theReserv);
 }
 
 void KbdMonitor::noteReadReady(int) {
 
 	switch (getch()) {
 		case '0': SwitchWin(0); break;
 
 		case KEY_LEFT: SwitchWin(TheWinPos-1); break;
 		case KEY_RIGHT: SwitchWin(TheWinPos+1); break;
 
 		case KEY_DOWN: SwitchLblFilter(TheFilter.lbl.pos - 1); break;
 		case KEY_UP: SwitchLblFilter(TheFilter.lbl.pos + 1); break;
 
+		case 'H':
+		case 'h':
+			if (!ShowingHelp) {
+				ShowingHelp = true;
+				touchwin(TheHelpWin->handle);
+				wrefresh(TheHelpWin->handle);
+				return;
+			} else
+				SwitchWin(TheWinPos);
+			break;
+
 		case 'S': SwitchCatFilter(TheFilter.logCat.pos - 1); break;
 		case 's': SwitchCatFilter(TheFilter.logCat.pos + 1); break;
 
 		case 'R': SwitchLblFilter(0); SwitchCatFilter(0); DeleteIdleHosts(); // fall through
 		case 'r': clearok(curscr, true); SwitchWin(TheWinPos); break;
 
 		case 'q':
 		case 'Q': DoShutdown = true; break;
 
 		default: return;
 	}
 
-	redrawwin(TheWins[TheWinPos]);
+	if (ShowingHelp)
+		ShowingHelp = false;
+
+	wrefresh(TheWins[TheWinPos]->handle);
 }
 
 /* MsgMonitor */
 
 MsgMonitor::MsgMonitor(Socket aSock): theSock(aSock), theSize(0) {
 	theReserv = TheScanner->setFD(theSock.fd(), dirRead, this);
 }
 
 MsgMonitor::~MsgMonitor() {
 	if (theReserv)
 		TheScanner->clearRes(theReserv);
 	if (theSock)
 		theSock.close();
 }
 
 void MsgMonitor::noteReadReady(int) {
 	static const int msgsz = sizeof(StatusFwdMsg);
 
 	const int sz = theSock.read(theBuf + theSize, sizeof(theBuf) - theSize);
 
@@ -491,41 +523,41 @@ void MsgMonitor::noteReadReady(int) {
 		cerr << "dispatcher at " << TheDisp << " quit." << endl;
 		DoShutdown = true;
 		return;
 	}
 
 	theSize += sz;
 
 	while (theSize >= msgsz) {
 		StatusFwdMsg msg;
 		memcpy(&msg, theBuf, msgsz);
 		theSize -= msgsz;
 		memmove(theBuf, theBuf + msgsz, theSize);
 
 		// handle the message
 		msg.ntoh();
 		msg.theRcvTime = TheClock;
 		NoteMsg(msg);
 	}
 	Assert(theSize >= 0);
 
-	wrefresh(TheWins[TheWinPos]);
+	wrefresh(TheWins[TheWinPos]->handle);
 }
 
 
 /* Ticker */
 
 Ticker::Ticker(Time aPeriod): thePeriod(aPeriod) {
 	sleepFor(thePeriod);
 }
 
 Ticker::~Ticker() {
 }
 
 void Ticker::wakeUp(const Alarm &alarm) {
 	AlarmUser::wakeUp(alarm);
 
 	DeleteIdleHosts();
 
 	sleepFor(thePeriod);
 }
 
@@ -560,216 +592,237 @@ bool Host::matches(const HostFilter &filter) const {
 }
 
 
 /* WinMatrix */
 
 WinMatrix::WinMatrix(int aMaxY, int aMaxX): 
 	theImage((aMaxY+1) * (aMaxX+1)), theMaxY(aMaxY), theMaxX(aMaxX) {
 	theImage.count(theImage.capacity());
 	theImage.memset(0);
 }
 
 int WinMatrix::safePos(int y, int x) const {
 	Assert(y <= theMaxY && x <= theMaxX);
 	const int p = y*(theMaxX+1) + x;
 	Assert(p < theImage.count());
 	return p;
 }
 
 /* Window */
 
-Window::Window(const char *aTitle): WINDOW(*newwin(0,0,0,0)), theTitle(aTitle) {
-	leaveok(this, true); // do not bother about cursor pos
+Window::Window(const char *aTitle): handle(newwin(0,0,0,0)), title(aTitle) {
+	leaveok(handle, true); // do not bother about cursor pos
 
 	int maxx, maxy;
-	getmaxyx(this, maxy, maxx);
+	(void)maxy; // silence g++ "set but not used" warning
+	getmaxyx(handle, maxy, maxx);
 
-	werase(this);
+	werase(handle);
 
 	// print window title
-	wattron(this, A_BOLD);
-	wattron(this, A_UNDERLINE);
-	mvwaddstr(this, 0, (maxx - strlen(theTitle))/2, (char*)theTitle);
-	wattroff(this, A_UNDERLINE);
-	wattroff(this, A_BOLD);
+	wattron(handle, A_BOLD);
+	wattron(handle, A_UNDERLINE);
+	mvwaddstr(handle, 0, (maxx - strlen(title))/2, (char*)title);
+	wattroff(handle, A_UNDERLINE);
+	wattroff(handle, A_BOLD);
 }
 
 Window::~Window() {
-	delwin(this);
+	delwin(handle);
 }
 
 
 /* InfoWindow */
 
 InfoWindow::InfoWindow(char const *aTitle): Window(aTitle),
 	theMsgCnt(0), theListnCnt(0) {
 	update();
 }
 
 void InfoWindow::update() {
 	const int x = 2;
 	int y = 3;
-	mvwprintw(this, y++, x, (char*)"%20s %s:%d", "dispatcher:", TheDisp.addrA().cstr(), TheDisp.port());
-	mvwprintw(this, y++, x, (char*)"%20s %7d", "listeners:", theListnCnt);
+	mvwprintw(handle, y++, x, (char*)"%20s %s:%d", "dispatcher:", TheDisp.addrA().cstr(), TheDisp.port());
+	mvwprintw(handle, y++, x, (char*)"%20s %7d", "listeners:", theListnCnt);
 	y++;
-	mvwprintw(this, y++, x, (char*)"%20s %7d", "messages received:", theMsgCnt);
-	mvwprintw(this, y++, x, (char*)"%20s %7d", "mean message size:", sizeof(StatusNotifMsg));
-	mvwprintw(this, y++, x, (char*)"%20s %7d", "selected hosts:", TheSelHostCnt);
-	mvwprintw(this, y++, x, (char*)"%20s %7d", "busy hosts:", TheBusyHostCnt);
-	mvwprintw(this, y++, x, (char*)"%20s %7d", "experiments:", TheUniqLblCnt);
+	mvwprintw(handle, y++, x, (char*)"%20s %7d", "messages received:", theMsgCnt);
+	mvwprintw(handle, y++, x, (char*)"%20s %7d", "mean message size:", sizeof(StatusNotifMsg));
+	mvwprintw(handle, y++, x, (char*)"%20s %7d", "selected hosts:", TheSelHostCnt);
+	mvwprintw(handle, y++, x, (char*)"%20s %7d", "busy hosts:", TheBusyHostCnt);
+	mvwprintw(handle, y++, x, (char*)"%20s %7d", "experiments:", TheUniqLblCnt);
 }
 
 void InfoWindow::noteAdd(const Host &host) {
 	Window::noteAdd(host);
 	update();
 }
 
 void InfoWindow::noteDel(const Host &host) {
 	Window::noteDel(host);
 	update();
 }
 
 void InfoWindow::noteUpd(const Host &host) {
 	Window::noteUpd(host);
 	if (host.log().depth()) {
 		theMsgCnt++;
 		theListnCnt = host.log()[0].theCopyCnt;
 	}
 	update();
 }
 
 
+/* HelpWindow */
+
+HelpWindow::HelpWindow(): Window("Help") {
+	update();
+}
+
+void HelpWindow::update() {
+	const int x = 2;
+	int y = 2;
+	mvwprintw(handle, y++, x, "0:\t\tMonitor Info.");
+	mvwprintw(handle, y++, x, "LEFT/RIGHT:\tNext/previous tab.");
+	mvwprintw(handle, y++, x, "UP/DOWN:\tChange label filters.");
+	mvwprintw(handle, y++, x, "h:\t\tShow/close this help tab.");
+	mvwprintw(handle, y++, x, "S/s:\t\tChange client/server-side filters.");
+	mvwprintw(handle, y++, x, "R:\t\tReset filters, remove idle hosts, and refresh screen.");
+	mvwprintw(handle, y++, x, "r:\t\tRefresh screen.");
+	mvwprintw(handle, y++, x, "Q/q:\t\tQuit.");
+}
+
+
 /* SumWindow */
 
 SumWindow::SumWindow(char const *aTitle): Window(aTitle) {
 	update();
 }
 
 void SumWindow::update() {
 
 	// collect summary info from hosts
 	MsgSum cltSum, srvSum;
 	for (int i = 0; i < TheHosts.count(); ++i) {
 		if (const Host *host = TheHosts[i]) {
 			if (host->log().depth()) {
 				const StatusFwdMsg &msg = host->log()[0];
 				if (host->isClient())
 					cltSum += msg;
 				else
 					srvSum += msg;
 			}
 		}
 	}
 
 	const int x = 2;
 
 	int y = 2;
-	mvwprintw(this, y++, x, (char*)"%s (%d hosts)", "Client side:", cltSum.hostCount());
+	mvwprintw(handle, y++, x, (char*)"%s (%d hosts)", "Client side:", cltSum.hostCount());
 	displaySide(y, x+2, cltSum);
 
 	y += 1;
-	mvwprintw(this, y++, x, (char*)"%s (%d hosts)", "Server side:", srvSum.hostCount());
+	mvwprintw(handle, y++, x, (char*)"%s (%d hosts)", "Server side:", srvSum.hostCount());
 	displaySide(y, x+2, srvSum);
 
 	y += 1;
-	mvwprintw(this, y++, x+2, (char*)"%20s %7s %8s %9s %9s %s",
+	mvwprintw(handle, y++, x+2, (char*)"%20s %7s %8s %9s %9s %s",
 		"measurement:", "min", "mean", "max", "sum", "unit");
 }
 
 void SumWindow::displaySide(int &y, int x, const MsgSum &sum) {
-	//mvwprintw(this, y++, x, (char*)"%20s %s", "labels:", 
+	//mvwprintw(handle, y++, x, (char*)"%20s %s", "labels:",
 	//	(const char*)sum.theLabels);
 
 	displayLine(y++, x, "load:", sum.theReqRate, "req/sec");
 	displayLine(y++, x, "throughput:", sum.theRepRate, "rep/sec");
 	//displayLine(y++, x, "bandwidth:", sum.theBwidth, "Mbit/sec", 1024.*1024/8);
 
 	displayLine(y++, x, "response time:", sum.theRespTime, "msec");
 	displayLine(y++, x, "DHR:", sum.theDHR, "%");
 	//displayLine(y++, x, "connection use:", sum.theConnUse, "xact/conn");
 
 	displayLine(y++, x, "errors total:", sum.theTotErrRatio, "%");
 	//displayLine(y++, x, "errors now:", sum.theErrRatio, "%");
 	displayLine(y++, x, "xactions total:", sum.theXactTotCnt, "x10^6", 1e6);
 	displayLine(y++, x, "open sockets now:", sum.theSockInstCnt, "");
 }
 
 void SumWindow::displayLine(int y, int x, const char *label, const AggrStat &stats, const char *meas, double scale) {
-	mvwprintw(this, y, x, (char*)"%20s %7d %8d %9d %9d %s",
+	mvwprintw(handle, y, x, (char*)"%20s %7d %8d %9d %9d %s",
 		label,
 		Dbl2Int(stats.min()/scale),
 		Dbl2Int(stats.mean()/scale), 
 		Dbl2Int(stats.max()/scale),
 		Dbl2Int(stats.sum()/scale),
 		meas);
 }
 
 void SumWindow::noteAdd(const Host &host) {
 	Window::noteAdd(host);
 	update();
 }
 
 void SumWindow::noteDel(const Host &host) {
 	Window::noteDel(host);
 	update();
 }
 
 void SumWindow::noteUpd(const Host &host) {
 	Window::noteUpd(host);
 	update();
 }
 
 
 /* SmxWindow */
 
 SmxWindow::SmxWindow(char const *aTitle): Window(aTitle) {
 
-	wattron(this, A_BOLD);
+	wattron(handle, A_BOLD);
 
 	// horizontal lables
 	for (int i = 0; i < 10; ++i)
-		mvwprintw(this, 2, Id2X(i), (char*)"%7d", i+1);
+		mvwprintw(handle, 2, Id2X(i), (char*)"%7d", i+1);
 
 	// vertical labels
 	for (int i = 0; i < 100; i += 10)
-		mvwprintw(this, Id2Y(i), 0, (char*)"%2d", i);
+		mvwprintw(handle, Id2Y(i), 0, (char*)"%2d", i);
 
-	wattroff(this, A_BOLD);
+	wattroff(handle, A_BOLD);
 }
 
 void SmxWindow::noteAdd(const Host &host) {
 	Window::noteAdd(host);
 	displayMsg(Host2Y(host), Host2X(host), host.log()[0]);
 }
 
 void SmxWindow::noteDel(const Host &host) {
 	Window::noteDel(host);
 	eraseSlot(Host2Y(host), Host2X(host));
 }
 
 void SmxWindow::noteUpd(const Host &host) {
 	Window::noteUpd(host);
 	displayMsg(Host2Y(host), Host2X(host), host.log()[0]);
 }
 
 void SmxWindow::eraseSlot(int y, int x) {
-	mvwprintw(this, y, x, (char*)"%7s ", "");
+	mvwprintw(handle, y, x, (char*)"%7s ", "");
 }
 
 int SmxWindow::Host2Y(const Host &host) {
 	return Id2Y((host.lna() & 255) - 1);
 }
 
 int SmxWindow::Host2X(const Host &host) {
 	return Id2X((host.lna() & 255) - 1);
 }
 
 int SmxWindow::Id2Y(int host_id) {
 	return TheYMargin + ((host_id % 100) / 10) * TheYCellWidth;
 }
 
 int SmxWindow::Id2X(int host_id) {
 	return TheXMargin + (host_id % 10) * TheXCellWidth;
 }
 
 
 /* NumxWindow */
@@ -781,98 +834,98 @@ NumxWindow::NumxWindow(const char *aTitle, const char *aFmt):
 	update();
 }
 
 void NumxWindow::noteAdd(const Host &host) {
 	theNumCnt++;
 	SmxWindow::noteAdd(host);
 }
 
 void NumxWindow::noteDel(const Host &host) {
 	theNumCnt--;
 	SmxWindow::noteDel(host);
 }
 
 void NumxWindow::noteUpd(const Host &host) {
 	SmxWindow::noteUpd(host);
 }
 
 void NumxWindow::displayMsg(int y, int x, const StatusFwdMsg &msg) {
 	const double n = msg2num(msg);
 	theNumSum -= theMatrix(y, x);
-	mvwprintw(this, y, x, (char*)theFmt, n);
+	mvwprintw(handle, y, x, (char*)theFmt, n);
 	theMatrix(y, x) = n;
 	theNumSum += n;
 	update();
 }
 
 void NumxWindow::eraseSlot(int y, int x) {
 	theNumSum -= theMatrix(y, x);
 	SmxWindow::eraseSlot(y, x);
 	theMatrix(y, x) = 0;
 	update();
 }
 
 void NumxWindow::update() {
 	int maxx, maxy;
-	getmaxyx(this, maxy, maxx);
+	getmaxyx(handle, maxy, maxx);
 
     int y = maxy-1;
 
-	wattron(this, A_BOLD);
-	mvwaddstr(this, y, Id2X(2) + TheXCellWidth-4, (char*)"cnt:");
-	mvwaddstr(this, y, Id2X(4) + TheXCellWidth-4, (char*)"avg:");
-	mvwaddstr(this, y, Id2X(6) + TheXCellWidth-4, (char*)"sum:");
-	wattroff(this, A_BOLD);
+	wattron(handle, A_BOLD);
+	mvwaddstr(handle, y, Id2X(2) + TheXCellWidth-4, (char*)"cnt:");
+	mvwaddstr(handle, y, Id2X(4) + TheXCellWidth-4, (char*)"avg:");
+	mvwaddstr(handle, y, Id2X(6) + TheXCellWidth-4, (char*)"sum:");
+	wattroff(handle, A_BOLD);
 
-	mvwprintw(this, y, Id2X(3), (char*)"%7d", theNumCnt);
-	mvwprintw(this, y, Id2X(5), (char*)theFmt, Ratio(theNumSum,theNumCnt));
-	mvwprintw(this, y, Id2X(7), (char*)theFmt, theNumSum);
+	mvwprintw(handle, y, Id2X(3), (char*)"%7d", theNumCnt);
+	mvwprintw(handle, y, Id2X(5), (char*)theFmt, Ratio(theNumSum,theNumCnt));
+	mvwprintw(handle, y, Id2X(7), (char*)theFmt, theNumSum);
 }
 
 
 /* RunTimeSmxWin */
 
 void RunTimeSmxWin::displayMsg(int y, int x, const StatusFwdMsg &msg) {
 	const int sec = (msg.theSndTime - msg.theStartTime).sec();
 	const int min = (sec/60) % 60;
 	const int hour = sec/3600;
 	if (hour > 0)
-		mvwprintw(this, y, x, (char*)"%4d:%02d", hour, min);
+		mvwprintw(handle, y, x, (char*)"%4d:%02d", hour, min);
 	else
-		mvwprintw(this, y, x, (char*)"%4d.%02d", min, sec%60);
+		mvwprintw(handle, y, x, (char*)"%4d.%02d", min, sec%60);
 }
 
 
 /* MsgGapSmxWin */
 
 void MsgGapSmxWin::noteUpd(const Host &host) {
 	if (host.log().depth() > 1)
 		theLastMsg = host.log()[1];
 	SmxWindow::noteUpd(host);
 }
 
 void MsgGapSmxWin::displayMsg(int y, int x, const StatusFwdMsg &msg) {
 	if (theLastMsg.theRcvTime >= 0)
-		mvwprintw(this, y, x, (char*)"%7d", 
+		mvwprintw(handle, y, x, (char*)"%7d",
 			(msg.theRcvTime - theLastMsg.theRcvTime).sec());
 	else
-		mvwprintw(this, y, x, (char*)"%7s", "?");
+		mvwprintw(handle, y, x, (char*)"%7s", "?");
 }
 
 MsgSum::MsgSum(): theLabels("") {
 }
 
 MsgSum &MsgSum::operator +=(const StatusFwdMsg &msg) {
 	/*if (msg.theLabel && !theLabels.str(msg.theLabel)) { // XXX: not sufficient
 		if (theLabels)
 			theLabels += ",";
 		theLabels += msg.theLabel;
 	}*/
 
 	theReqRate.record(Dbl2Int(msg.theReqRate));
 	theRepRate.record(Dbl2Int(msg.theRepRate));
 	theBwidth.record(Dbl2Int(msg.theBwidth));
 	theRespTime.record(msg.theRespTime.msec());
 	theDHR.record(Dbl2Int(msg.theDHR*100));
 	theConnUse.record(Dbl2Int(msg.theConnUse));
 	theErrRatio.record(Dbl2Int(msg.theErrRatio*100));
 	theXactTotCnt.record(msg.theXactTotCnt);
@@ -963,40 +1016,43 @@ void DeleteIdleHosts() {
 		if (Host *host = TheHosts[h]) {
 			if (host->busy()) {
 				// XXX pre-IPv6 code used 's_addr' as an int
 				const int idx = host->addr().addrN().octet(0) % TheHostIdx.count();
 				TheHostIdx[idx] = host;
 				if (host->selected())
 					TheSelHostCnt++;
 			} else {
 				TheBusyHostCnt--;
 				Broadcast(&Window::noteDel, *host);
 				DelLastLabel(host, host->runLabel());
 				delete host;
 				TheHosts[h] = 0;
 			}
 		}
 	}
 }
 
 static
 bool AddFirstLabel(const Host *skip, const String &l) {
+	if (!l)
+		return false; // empty label
+
 	for (int h = 0; h < TheHosts.count(); ++h) {
 		if (TheHosts[h] == skip)
 			continue;
 		if (TheHosts[h] && l == TheHosts[h]->runLabel())
 			return false; // not first label
 	}
 	TheUniqLblCnt++;
 	for (int i = 1; i < TheLabels.count(); ++i) {
 		if (!*TheLabels[i]) {
 			*TheLabels[i] = l;
 			return true;
 		}
 	}
 	TheLabels.append(new String(l));
 	return false;
 }
 
 static
 bool DelLastLabel(const Host *skip, const String &l) {
 	for (int h = 0; h < TheHosts.count(); ++h) {
@@ -1004,83 +1060,83 @@ bool DelLastLabel(const Host *skip, const String &l) {
 			continue;
 		if (TheHosts[h] && l == TheHosts[h]->runLabel())
 			return false; // not last label
 	}
 	TheUniqLblCnt--;
 	for (int i = 1; i < TheLabels.count(); ++i) {
 		if (*TheLabels[i] == l) {
 			*TheLabels[i] = 0;
 			if (i == TheLabels.count()-1)
 				delete TheLabels.pop();
 			return true;
 		}
 	}
 	Assert(0);
 	return false;
 }
 
 static
 int SwitchWin(int idx) {
 	TheWinPos = (idx + TheWins.count()) % TheWins.count();
-	touchwin(TheWins[TheWinPos]);
+	touchwin(TheWins[TheWinPos]->handle);
 	return TheWinPos;
 }
 
 static
 void SelectHost(Host *host) {
 	const bool wasSel = host->selected();
 	const bool isSel = host->matches(TheFilter);
 	if (!wasSel && isSel) {
 		host->selected(isSel);
 		TheSelHostCnt++;
 		Broadcast(&Window::noteAdd, *host);
 	} else
 	if (wasSel && !isSel) {
 		TheSelHostCnt--;
 		Broadcast(&Window::noteDel, *host);
 		host->selected(isSel);
 	}
 }
 
 static
 void SelectHosts() {
 	for (int h = 0; h < TheHosts.count(); ++h) {
 		if (Host *host = TheHosts[h])
 			SelectHost(host);
 	}
 
 	// update selection warnings
 	ofixedstream s(TheFiltWarn, sizeof(TheFiltWarn));
 	BuildFiltWarn(s);
 	for (int w = 0; w < TheWins.count(); ++w) {
 		if (strlen(TheFiltWarn)) {
-			wattron(TheWins[w], A_BOLD);
-			mvwaddstr(TheWins[w], 1, 0, (char*)"Filters:");
-			wattroff(TheWins[w], A_BOLD);
-			mvwprintw(TheWins[w], 1, 10, (char*)"%-20s", TheFiltWarn);
+			wattron(TheWins[w]->handle, A_BOLD);
+			mvwaddstr(TheWins[w]->handle, 1, 0, (char*)"Filters:");
+			wattroff(TheWins[w]->handle, A_BOLD);
+			mvwprintw(TheWins[w]->handle, 1, 10, (char*)"%-20s", TheFiltWarn);
 		} else {
-			wattroff(TheWins[w], A_BOLD);
-			mvwprintw(TheWins[w], 1, 0, (char*)"%-30s", "");
+			wattroff(TheWins[w]->handle, A_BOLD);
+			mvwprintw(TheWins[w]->handle, 1, 0, (char*)"%-30s", "");
 		}
 	}
 
-	touchwin(TheWins[TheWinPos]);
+	touchwin(TheWins[TheWinPos]->handle);
 }
 
 static
 int SwitchLblFilter(int idx) {
 	TheFilter.lbl.pos = (idx + TheLabels.count()) % TheLabels.count();
 
 	if (TheFilter.lbl.pos)
 		for (int i = 0; !*TheLabels[TheFilter.lbl.pos] && i < TheLabels.count(); ++i) {
 			TheFilter.lbl.pos++;
 			TheFilter.lbl.pos %= TheLabels.count();
 		}
 
 	SelectHosts();
 	return TheFilter.lbl.pos;
 }
 
 static
 int SwitchCatFilter(int idx) {
 	TheFilter.logCat.pos = (idx + lgcEnd) % lgcEnd;
 	SelectHosts();
@@ -1138,52 +1194,55 @@ int main(int argc, char *argv[]) {
 	TheHostIdx.count(TheHostIdx.capacity());
 	TheLabels.append(new String);
 
 	TheWins.append(TheInfoWin = new InfoWindow("Monitor Info"));
 	TheWins.append(new SumWindow("Summary Information"));
 	TheWins.append(new RunLabelSmxWin("Experiment Label"));
 	TheWins.append(new RunTimeSmxWin("Run time [h:m.s]"));
 	TheWins.append(new ReqRateSmxWin("Load [requests/sec]"));
 	TheWins.append(new RepRateSmxWin("Throughput [replies/sec]"));
 	TheWins.append(new BwidthSmxWin("Network Bandwidth [Mbps,replies]"));
 	TheWins.append(new RespTimeSmxWin("Response Time [msec]"));
 	TheWins.append(new DHRSmxWin("DHR [%]"));
 	TheWins.append(new ConnUseSmxWin("Connection Use [xact/conn]"));
 	TheWins.append(new ErrTotRatioSmxWin("Errors Total [%]"));
 	TheWins.append(new ErrTotCntSmxWin("Errors Total Count x 1000"));
 	TheWins.append(new XactTotCntSmxWin("Total Xaction Count x 1000"));
 	TheWins.append(new ErrRatioSmxWin("Errors Now [%]"));
 	TheWins.append(new SockInstCntSmxWin("Open Sockets Now"));
 	TheWins.append(new MsgGapSmxWin("Message Gap [sec]"));
 
+	TheHelpWin = new HelpWindow;
+
 	signal(SIGPIPE, SIG_IGN);
 	Clock::Update();
 
 	TheScanner = new PG_PREFFERED_FILE_SCANNER;
 	TheScanner->configure();
 
 	MsgMonitor *msgSrv = new MsgMonitor(sock);
 	KbdMonitor *kbdSrv = new KbdMonitor(0); // XXX: how to get stdin fd?
 	Ticker *ticker = new Ticker(Time::Sec(60));
 
 	SwitchWin(0);
-	redrawwin(TheWins[TheWinPos]);
+	wrefresh(TheWins[TheWinPos]->handle);
 
 	while (!DoShutdown) {
 		Clock::Update();
 		Time tout = TheAlarmClock.on() ? 
 			TheAlarmClock.timeLeft() : Time();
 		TheScanner->scan(TheAlarmClock.on() ? 0 : &tout);
 	}
 
 	delete ticker;
 	delete msgSrv;
 	delete kbdSrv;
+	delete TheHelpWin;
 	delete TheScanner;
 
 	endwin();
 
 	return 0;
 }
 
 
 #endif /* HAVE_NCURSES_H */