Blob Blame History Raw
/*
 * Copyright (c) 2013, Intel Corporation
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 *   * Redistributions of source code must retain the above copyright notice,
 *     this list of conditions and the following disclaimer.
 *   * Redistributions in binary form must reproduce the above copyright
 *     notice, this list of conditions and the following disclaimer in the
 *     documentation and/or other materials provided with the distribution.
 *   * Neither the name of Intel Corporation nor the names of its contributors
 *     may be used to endorse or promote products derived from this software
 *     without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

/* This file contains code to create/show/destroy a window on screen. */

#include <inttypes.h>
#include <stdlib.h>
#include <sys/types.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <strings.h>
#include <sys/mman.h>
#include <signal.h>
#include <curses.h>
#include "include/types.h"
#include "include/util.h"
#include "include/disp.h"
#include "include/reg.h"
#include "include/lwp.h"
#include "include/proc.h"
#include "include/page.h"
#include "include/perf.h"
#include "include/os/node.h"
#include "include/os/plat.h"
#include "include/os/os_util.h"
#include "include/os/os_win.h"

static boolean_t s_first_load = B_TRUE;
static win_reg_t s_note_reg;
static win_reg_t s_title_reg;

/*
 * Build the readable string for caption line.
 * (window type: "WIN_TYPE_TOPNPROC")
 */
static void
topnproc_caption_build(char *buf, int size)
{
	char tmp[32];

	switch (g_sortkey) {
	case SORT_KEY_CPU:
		(void) snprintf(tmp, sizeof (tmp), "*%s", CAPTION_CPU);
		(void) snprintf(buf, size,
		    "%6s%15s%11s%11s%11s%11s%11s",
		    CAPTION_PID, CAPTION_PROC, CAPTION_RPI,
		    CAPTION_LPI, CAPTION_RL, CAPTION_CPI, tmp);
		break;

	case SORT_KEY_CPI:
		(void) snprintf(tmp, sizeof (tmp), "*%s", CAPTION_CPI);
		(void) snprintf(buf, size,
		    "%6s%15s%11s%11s%11s%11s%11s",
		    CAPTION_PID, CAPTION_PROC, CAPTION_RPI,
		    CAPTION_LPI, CAPTION_RL, tmp, CAPTION_CPU);
		break;

	case SORT_KEY_RPI:
		(void) snprintf(tmp, sizeof (tmp), "*%s", CAPTION_RPI);
		(void) snprintf(buf, size,
		    "%6s%15s%11s%11s%11s%11s%11s",
		    CAPTION_PID, CAPTION_PROC, tmp,
		    CAPTION_LPI, CAPTION_RL, CAPTION_CPI, CAPTION_CPU);
		break;

	case SORT_KEY_LPI:
		(void) snprintf(tmp, sizeof (tmp), "*%s", CAPTION_LPI);
		(void) snprintf(buf, size,
		    "%6s%15s%11s%11s%11s%11s%11s",
		    CAPTION_PID, CAPTION_PROC, CAPTION_RPI,
		    tmp, CAPTION_RL, CAPTION_CPI, CAPTION_CPU);
		break;

	case SORT_KEY_RL:
		(void) snprintf(tmp, sizeof (tmp), "*%s", CAPTION_RL);
		(void) snprintf(buf, size,
		    "%6s%15s%11s%11s%11s%11s%11s",
		    CAPTION_PID, CAPTION_PROC, CAPTION_RPI,
		    CAPTION_LPI, tmp, CAPTION_CPI, CAPTION_CPU);
		break;

	default:
		(void) snprintf(buf, size,
		    "%6s%15s%11s%11s%11s%11s%11s",
		    CAPTION_PID, CAPTION_PROC, CAPTION_RPI,
		    CAPTION_LPI, CAPTION_RL, CAPTION_CPI, CAPTION_CPU);
		break;
	}
}

static void
topnproc_data_build(char *buf, int size, topnproc_line_t *line)
{
	win_countvalue_t *value = &line->value;

	(void) snprintf(buf, size,
	    "%6d%15s%11.1f%11.1f%11.1f%11.2f%10.1f",
	    line->pid, line->proc_name, value->rpi, value->lpi,
	    value->rl, value->cpi, value->cpu * 100);
}

/*
 * Build the readable string for data line.
 * (window type: "WIN_TYPE_TOPNPROC")
 */
static void
topnproc_str_build(char *buf, int size, int idx, void *pv)
{
	topnproc_line_t *lines = (topnproc_line_t *)pv;
	topnproc_line_t *line = &lines[idx];

	topnproc_data_build(buf, size, line);
}

/*
 * Build the readable string for scrolling line.
 * (window type: "WIN_TYPE_TOPNPROC")
 */
static void
topnproc_line_get(win_reg_t *r, int idx, char *line, int size)
{
	topnproc_line_t *lines;

	lines = (topnproc_line_t *)(r->buf);
	topnproc_str_build(line, size, idx, (void *)lines);
}

/*
 * Build the readable string for caption line.
 * (window type: "WIN_TYPE_RAW_NUM")
 */
static void
rawnum_caption_build(char *buf, int size)
{
	char tmp[32];

	switch (g_sortkey) {
	case SORT_KEY_CPU:
		(void) snprintf(tmp, sizeof (tmp), "*%s", CAPTION_CPU);
		(void) snprintf(buf, size,
		    "%6s%15s%11s%11s%11s%11s%11s",
		    CAPTION_PID, CAPTION_PROC, CAPTION_RMA,
		    CAPTION_LMA, CAPTION_RL, CAPTION_CPI, tmp);
		break;

	case SORT_KEY_CPI:
		(void) snprintf(tmp, sizeof (tmp), "*%s", CAPTION_CPI);
		(void) snprintf(buf, size,
		    "%6s%15s%11s%11s%11s%11s%11s",
		    CAPTION_PID, CAPTION_PROC, CAPTION_RMA,
		    CAPTION_LMA, CAPTION_RL, tmp, CAPTION_CPU);
		break;

	case SORT_KEY_RMA:
		(void) snprintf(tmp, sizeof (tmp), "*%s", CAPTION_RMA);
		(void) snprintf(buf, size,
		    "%6s%15s%11s%11s%11s%11s%11s",
		    CAPTION_PID, CAPTION_PROC, tmp,
		    CAPTION_LMA, CAPTION_RL, CAPTION_CPI, CAPTION_CPU);
		break;

	case SORT_KEY_LMA:
		(void) snprintf(tmp, sizeof (tmp), "*%s", CAPTION_LMA);
		(void) snprintf(buf, size,
		    "%6s%15s%11s%11s%11s%11s%11s",
		    CAPTION_PID, CAPTION_PROC, CAPTION_RMA,
		    tmp, CAPTION_RL, CAPTION_CPI, CAPTION_CPU);
		break;

	case SORT_KEY_RL:
		(void) snprintf(tmp, sizeof (tmp), "*%s", CAPTION_RL);
		(void) snprintf(buf, size,
		    "%6s%15s%11s%11s%11s%11s%11s",
		    CAPTION_PID, CAPTION_PROC, CAPTION_RMA,
		    CAPTION_LMA, tmp, CAPTION_CPI, CAPTION_CPU);
		break;

	default:
		(void) snprintf(buf, size,
		    "%6s%15s%11s%11s%11s%11s%11s",
		    CAPTION_PID, CAPTION_PROC, CAPTION_RMA,
		    CAPTION_LMA, CAPTION_RL, CAPTION_CPI, CAPTION_CPU);
		break;
	}
}

static void
rawnum_data_build(char *buf, int size, topnproc_line_t *line)
{
	win_countvalue_t *value = &line->value;

	(void) snprintf(buf, size,
	    "%6d%15s%11.1f%11.1f%11.1f%11.2f%10.1f",
	    line->pid, line->proc_name, value->rma, value->lma,
	    value->rl, value->cpi, value->cpu * 100);
}

/*
 * Build the readable string of data line
 * (window type: "WIN_TYPE_RAW_NUM")
 */
static void
rawnum_str_build(char *buf, int size, int idx, void *pv)
{
	topnproc_line_t *lines = (topnproc_line_t *)pv;
	topnproc_line_t *line = &lines[idx];

	rawnum_data_build(buf, size, line);
}

/*
 * Build the readable string for scrolling line.
 * (window type: "WIN_TYPE_RAW_NUM")
 */
static void
rawnum_line_get(win_reg_t *r, int idx, char *line, int size)
{
	topnproc_line_t *lines;

	lines = (topnproc_line_t *)(r->buf);
	rawnum_str_build(line, size, idx, (void *)lines);
}

/*
 * Initialize the display layout.
 * (window type: "WIN_TYPE_TOPNPROC"/"WIN_TYPE_RAW_NUM")
 */
static dyn_topnproc_t *
topnproc_dyn_create(int type)
{
	dyn_topnproc_t *dyn;
	void *buf;
	int i;

	if ((buf = zalloc(sizeof (topnproc_line_t) *
	    WIN_NLINES_MAX)) == NULL) {
		return (NULL);
	}
	if ((dyn = zalloc(sizeof (dyn_topnproc_t))) == NULL) {
		free(buf);
		return (NULL);
	}


	if ((i = reg_init(&dyn->summary, 0, 1, g_scr_width, 2, A_BOLD)) < 0)
		goto L_EXIT;
	if ((i = reg_init(&dyn->caption, 0, i, g_scr_width, 2, A_BOLD | A_UNDERLINE)) < 0)
		goto L_EXIT;
	if ((i = reg_init(&dyn->data, 0, i, g_scr_width, g_scr_height - i - 5, 0)) < 0)
		goto L_EXIT;

	if (type == WIN_TYPE_TOPNPROC) {
		reg_buf_init(&dyn->data, buf, topnproc_line_get);
	} else {
		reg_buf_init(&dyn->data, buf, rawnum_line_get);
	}

	reg_scroll_init(&dyn->data, B_TRUE);
	(void) reg_init(&dyn->hint, 0, i, g_scr_width,
	    g_scr_height - i - 1, A_BOLD);
	return (dyn);
L_EXIT:
	free(dyn);
	free(buf);
	return (NULL);
}

/*
 * Release the resources of window.
 * (window type: "WIN_TYPE_TOPNPROC"/"WIN_TYPE_RAW_NUM")
 */
static void
topnproc_win_destroy(dyn_win_t *win)
{
	dyn_topnproc_t *dyn;

	if ((dyn = win->dyn) != NULL) {
		if (dyn->data.buf != NULL) {
			free(dyn->data.buf);
			dyn->data.buf = NULL;
		}

		reg_win_destroy(&dyn->summary);
		reg_win_destroy(&dyn->caption);
		reg_win_destroy(&dyn->data);
		reg_win_destroy(&dyn->hint);
		free(dyn);
	}
}

/*
 * Seperate the value of metrics by raw perf data.
 */
static int
win_countvalue_fill(win_countvalue_t *cv,
	count_value_t *countval_arr, int nid, int ms, int ncpus)
{
	uint64_t rma, lma, ir, clk, all_clks;
	double d;

	rma = node_countval_sum(countval_arr, nid, UI_COUNT_RMA);
	lma = node_countval_sum(countval_arr, nid, UI_COUNT_LMA);
	clk = node_countval_sum(countval_arr, nid, UI_COUNT_CLK);
	ir = node_countval_sum(countval_arr, nid, UI_COUNT_IR);

	cv->rpi = ratio(rma * 1000, ir);
	cv->lpi = ratio(lma * 1000, ir);
	cv->cpi = ratio(clk, ir);
	cv->rma = ratio(rma, 1000);
	cv->lma = ratio(lma, 1000);
	cv->rl = ratio(rma, lma);

	d = (double)ms / MS_SEC;
	all_clks = (uint64_t)(d * (double)g_clkofsec * (double)ncpus);
	cv->cpu = ratio(clk, all_clks);
	return (0);
}

/*
 * Convert the perf data to the required format and copy
 * the converted result out via "line".
 * (window type: "WIN_TYPE_TOPNPROC")
 */
static void
topnproc_data_save(track_proc_t *proc, int intval, topnproc_line_t *line)
{
	(void) memset(line, 0, sizeof (topnproc_line_t));

	/*
	 * Cut off the process name if it's too long.
	 */
	(void) strncpy(line->proc_name, proc->name, sizeof (line->proc_name));
	line->proc_name[WIN_PROCNAME_SIZE - 1] = 0;
	line->pid = proc->pid;
	line->nlwp = proc_nlwp(proc);

	(void) win_countvalue_fill(&line->value, proc->countval_arr,
	    NODE_ALL, intval, g_ncpus);
}

static void
topnproc_data_show(dyn_win_t *win)
{
	dyn_topnproc_t *dyn;
	win_reg_t *r, *data_reg;
	char content[WIN_LINECHAR_MAX], intval_buf[16];
	int nprocs, nlwps, i;
	track_proc_t *proc;
	int intval;
	topnproc_line_t *lines;

	dyn = (dyn_topnproc_t *)(win->dyn);
	data_reg = &dyn->data;

	/* Get the number of total processes and total threads */
	proc_lwp_count(&nprocs, &nlwps);
	nprocs = MIN(nprocs, WIN_NLINES_MAX);
	data_reg->nlines_total = nprocs;

	/*
	 * Convert the sampling interval (nanosecond) to
	 * a human readable string.
	 */
	disp_intval(intval_buf, 16);

	/*
	 * Display the summary message:
	 * "Monitoring xxx processes and yyy threads (interval: zzzs)"
	 */
	(void) snprintf(content, sizeof (content),
	    "Monitoring %d processes and %d threads (interval: %s)",
	    nprocs, nlwps, intval_buf);

	r = &dyn->summary;
	reg_erase(r);
	reg_line_write(r, 1, ALIGN_LEFT, content);
	dump_write("\n*** %s\n", content);
	reg_refresh_nout(r);

	/*
	 * Display the caption of table:
	 * "PID PROC/NLWP RMA(K) LMA(K) CPI CPU%"
	 */
	r = &dyn->caption;
	if (win->type == WIN_TYPE_TOPNPROC) {
		topnproc_caption_build(content, sizeof (content));
	} else {
		rawnum_caption_build(content, sizeof (content));
	}

	reg_erase(r);
	reg_line_write(r, 1, ALIGN_LEFT, content);
	dump_write("%s\n", content);
	reg_refresh_nout(r);

	reg_erase(data_reg);
	lines = (topnproc_line_t *)(data_reg->buf);

	/*
	 * Sort the processes by specified metric which
	 * is indicated by g_sortkey
	 */
	proc_group_lock();
	proc_resort(g_sortkey);

	/*
	 * Save the perf data of processes in scrolling buffer.
	 */
	for (i = 0; i < nprocs; i++) {
		if ((proc = proc_sort_next()) == NULL) {
			break;
		}

		intval = proc_intval_get(proc);
		topnproc_data_save(proc, intval, &lines[i]);
	}

	/*
	 * Display the processes with metrics in scrolling buffer
	 */
	if (win->type == WIN_TYPE_TOPNPROC) {
		reg_scroll_show(data_reg, (void *)lines, nprocs,
		    topnproc_str_build);
	} else {
		reg_scroll_show(data_reg, (void *)lines, nprocs,
		    rawnum_str_build);
	}

	proc_group_unlock();
	reg_refresh_nout(data_reg);

	/*
	 * Dispaly hint message for window type
	 * "WIN_TYPE_TOPNPROC" and "WIN_TYPE_RAW_NUM"
	 */
	r = &dyn->hint;
	reg_erase(r);

	if (win->type == WIN_TYPE_TOPNPROC) {
		reg_line_write(r, 1, ALIGN_LEFT,
		    "<- Hotkey for sorting: 1(RPI), 2(LPI), 3(RMA/LMA), "
		    "4(CPI), 5(CPU%%) ->");
	} else {
		reg_line_write(r, 1, ALIGN_LEFT,
		    "<- Hotkey for sorting: 1(RMA), 2(LMA), 3(RMA/LMA), "
		    "4(CPI), 5(CPU%%) ->");
	}

	reg_line_write(r, 2, ALIGN_LEFT,
	    "CPU%% = system CPU utilization");

	reg_refresh_nout(r);
}

/*
 * Show the message "Loading ..." on screen
 */
static void
load_msg_show(void)
{
	char content[64];
	win_reg_t r;

	(void) snprintf(content, sizeof (content), "Loading ...");

	(void) reg_init(&r, 0, 1, g_scr_width, g_scr_height - 1, A_BOLD);
	reg_erase(&r);
	reg_line_write(&r, 1, ALIGN_LEFT, content);
	reg_refresh(&r);
	reg_win_destroy(&r);
}

/*
 * Show the title "NumaTop v2.0, (C) 2015 Intel Corporation"
 */
void
win_title_show(void)
{
	reg_erase(&s_title_reg);
	reg_line_write(&s_title_reg, 0, ALIGN_MIDDLE, NUMATOP_TITLE);
	reg_refresh_nout(&s_title_reg);
}

/*
 * Show the note information at the bottom of window"
 */
void
win_note_show(char *note)
{
	char *content;
	char *p;

	p = NOTE_DEFAULT;

	content = (note != NULL) ? note : p;
	reg_erase(&s_note_reg);
	reg_line_write(&s_note_reg, 0, ALIGN_LEFT, content);
	reg_refresh(&s_note_reg);
	reg_update_all();
}

/*
 * Display window on screen.
 * (window type: "WIN_TYPE_TOPNPROC" and "WIN_TYPE_RAW_NUM")
 */
static boolean_t
topnproc_win_draw(dyn_win_t *win)
{
	char *note;

	win_title_show();
	if (s_first_load) {
		s_first_load = B_FALSE;
		load_msg_show();
		win_note_show(NULL);
		reg_update_all();
		return (B_TRUE);
	}

	topnproc_data_show(win);

	if (win->type == WIN_TYPE_TOPNPROC) {
		note = NOTE_TOPNPROC;
		win_note_show(note);
	} else {
		note = NOTE_TOPNPROC_RAW;
		win_note_show(note);
	}

	reg_update_all();
	return (B_TRUE);
}

/*
 * The function would be called when user hits the <UP>/<DOWN> key
 * to scroll data line.
 * (window type: "WIN_TYPE_TOPNPROC" and "WIN_TYPE_RAW_NUM")
 */
static void
topnproc_win_scroll(dyn_win_t *win, int scroll_type)
{
	dyn_topnproc_t *dyn = (dyn_topnproc_t *)(win->dyn);

	reg_line_scroll(&dyn->data, scroll_type);
}

/*
 * The function would be called when user hits the "ENTER" key
 * on selected data line.
 * (window type: "WIN_TYPE_TOPNPROC" and "WIN_TYPE_RAW_NUM")
 */
static void
topnproc_win_scrollenter(dyn_win_t *win)
{
	dyn_topnproc_t *dyn = (dyn_topnproc_t *)(win->dyn);
	win_reg_t *r = &dyn->data;
	scroll_line_t *scroll = &r->scroll;
	topnproc_line_t *lines;
	cmd_monitor_t cmd_monitor;
	boolean_t badcmd;

	if (scroll->highlight == -1) {
		return;
	}

	/*
	 * Construct a command to switch to next window
	 * (WIN_TYPE_MONIPROC).
	 */
	lines = (topnproc_line_t *)(r->buf);
	cmd_monitor.id = CMD_MONITOR_ID;
	cmd_monitor.pid = lines[scroll->highlight].pid;
	cmd_monitor.lwpid = 0;

	/* LINTED E_BAD_PTR_CAST_ALIGN */
	cmd_execute((cmd_t *)(&cmd_monitor), &badcmd);
}

/*
 * Build the readable string for caption line.
 * (window type: "WIN_TYPE_MONIPROC"/"WIN_TYPE_MONILWP")
 */
static void
moni_caption_build(char *buf, int size)
{
	(void) snprintf(buf, size,
	    "%5s%10s%10s%11s%11s%10s%10s%10s",
	    CAPTION_NID, CAPTION_RPI, CAPTION_LPI,
	    CAPTION_RMA, CAPTION_LMA, CAPTION_RL,
	    CAPTION_CPI, CAPTION_CPU);
}

static void
moni_data_build(char *buf, int size, moni_line_t *line,
	node_t *node)
{
	win_countvalue_t *value = &line->value;

	(void) snprintf(buf, size,
	    "%5d%10.1f%10.1f%11.1f%11.1f%10.1f%10.2f%9.1f",
	    node->nid, value->rpi, value->lpi, value->rma, value->lma,
	    value->rl, value->cpi, value->cpu * 100);
}

/*
 * Build the readable string for data line.
 * (window type: "WIN_TYPE_MONIPROC"/"WIN_TYPE_MONILWP")
 */
static void
moni_str_build(char *buf, int size, int idx, void *pv)
{
	moni_line_t *lines = (moni_line_t *)pv;
	moni_line_t *line = &lines[idx];
	node_t *node;

	if ((node = node_valid_get(idx)) != NULL) {
		moni_data_build(buf, size, line, node);
	}
}

/*
 * Build the readable string for scrolling line.
 * (window type: "WIN_TYPE_MONIPROC"/"WIN_TYPE_MONILWP)
 */
static void
moni_line_get(win_reg_t *r, int idx, char *line, int size)
{
	moni_line_t *lines;

	lines = (moni_line_t *)(r->buf);
	moni_str_build(line, size, idx, (void *)lines);
}

/*
 * Initialize the display layout.
 * (window type: "WIN_TYPE_MONIPROC")
 */
static dyn_moniproc_t *
moniproc_dyn_create(pid_t pid)
{
	dyn_moniproc_t *dyn;
	void *buf_cur;
	int i, nnodes;

	nnodes = node_num();
	if ((buf_cur = zalloc(sizeof (moni_line_t) * nnodes)) == NULL) {
		return (NULL);
	}
	if ((dyn = zalloc(sizeof (dyn_moniproc_t))) == NULL) {
		free(buf_cur);
		return (NULL);
	}

	dyn->pid = pid;

	if ((i = reg_init(&dyn->msg, 0, 1, g_scr_width, 2, A_BOLD)) < 0)
		goto L_EXIT;
	if ((i = reg_init(&dyn->caption_cur, 0, i, g_scr_width, 2,
	    A_BOLD | A_UNDERLINE)) < 0)
		goto L_EXIT;
	if ((i = reg_init(&dyn->data_cur, 0, i, g_scr_width, nnodes, 0)) < 0)
		goto L_EXIT;

	reg_buf_init(&dyn->data_cur, buf_cur, moni_line_get);
	reg_scroll_init(&dyn->data_cur, B_TRUE);

	(void) reg_init(&dyn->hint, 0, i, g_scr_width,
	    g_scr_height - i - 1, A_BOLD);
	return (dyn);
L_EXIT:
	free(dyn);
	free(buf_cur);
	return (NULL);
}

/*
 * Initialize the display layout.
 * (window type: "WIN_TYPE_MONILWP")
 */
static dyn_monilwp_t *
monilwp_dyn_create(pid_t pid, id_t lwpid)
{
	dyn_monilwp_t *dyn;
	void *buf_cur;
	int i, nnodes;

	nnodes = node_num();
	if ((buf_cur = zalloc(sizeof (moni_line_t) * nnodes)) == NULL) {
		return (NULL);
	}
	if ((dyn = zalloc(sizeof (dyn_monilwp_t))) == NULL) {
		free(buf_cur);
		return (NULL);
	}

	dyn->pid = pid;
	dyn->lwpid = lwpid;

	if ((i = reg_init(&dyn->msg, 0, 1, g_scr_width, 2, A_BOLD)) < 0)
		goto L_EXIT;
	if ((i = reg_init(&dyn->caption_cur, 0, i, g_scr_width, 2,
	    A_BOLD | A_UNDERLINE)) < 0)
		goto L_EXIT;
	if ((i = reg_init(&dyn->data_cur, 0, i, g_scr_width, nnodes, 0)) < 0)
		goto L_EXIT;

	reg_buf_init(&dyn->data_cur, buf_cur, moni_line_get);
	reg_scroll_init(&dyn->data_cur, B_TRUE);

	(void) reg_init(&dyn->hint, 0, i, g_scr_width,
	    g_scr_height - i - 1, A_BOLD);
	return (dyn);
L_EXIT:
	free(dyn);
	free(buf_cur);
	return (NULL);
}

/*
 * Convert the perf data to the required format and copy
 * the converted result out via "line".
 * (window type: "WIN_TYPE_MONIPROC")
 */
/* ARGSUSED */
static void
moniproc_data_save(track_proc_t *proc, int nid_idx,
	int nnodes __attribute__((unused)),
	moni_line_t *line)
{
	int ncpus, intval;
	node_t *node;

	(void) memset(line, 0, sizeof (moni_line_t));
	if ((node = node_valid_get(nid_idx)) == NULL) {
		return;
	}

	line->nid = node->nid;
	line->pid = proc->pid;
	ncpus = node_ncpus(node);
	intval = proc_intval_get(proc);

	(void) win_countvalue_fill(&line->value, proc->countval_arr,
	    node->nid, intval, ncpus);
}

void
win_invalid_proc(void)
{
	win_warn_msg(WARN_INVALID_PID);
	win_note_show(NOTE_INVALID_PID);
	(void) sleep(GO_HOME_WAIT);
	disp_go_home();
}

static boolean_t
moniproc_data_show(dyn_win_t *win, boolean_t *note_out)
{
	dyn_moniproc_t *dyn;
	win_reg_t *r;
	char content[WIN_LINECHAR_MAX], intval_buf[16];
	pid_t pid;
	track_proc_t *proc;
	int i, nnodes;
	moni_line_t *lines;

	dyn = (dyn_moniproc_t *)(win->dyn);
	pid = dyn->pid;

	*note_out = B_FALSE;
	if ((proc = proc_find(pid)) == NULL) {
		win_invalid_proc();
		*note_out = B_TRUE;
		return (B_FALSE);
	}

	r = &dyn->msg;
	disp_intval(intval_buf, 16);
	(void) snprintf(content, sizeof (content),
	    "Monitoring the process \"%s\" (%d) (interval: %s)",
	    proc->name, proc->pid, intval_buf);

	reg_erase(r);
	reg_line_write(r, 1, ALIGN_LEFT, content);
	dump_write("\n*** %s\n", content);
	reg_refresh_nout(r);

	/*
	 * Display the caption of table:
	 * "NODE RPI(K) LPI(K) RMA(K) LMA(K) RMA/LMA CPI CPU%
	 */
	moni_caption_build(content, sizeof (content));
	r = &dyn->caption_cur;
	reg_erase(r);
	reg_line_write(r, 1, ALIGN_LEFT, content);
	dump_write("%s\n", content);
	reg_refresh_nout(r);

	nnodes = node_num();
	r = &dyn->data_cur;
	reg_erase(r);
	lines = (moni_line_t *)(r->buf);
	r->nlines_total = nnodes;

	/*
	 * Save the per-node data with metrics of a specified process
	 * in scrolling buffer.
	 */
	for (i = 0; i < nnodes; i++) {
		moniproc_data_save(proc, i, nnodes, &lines[i]);
	}

	/*
	 * Display the per-node data with metrics of a specified process
	 * in scrolling buffer
	 */
	reg_scroll_show(r, (void *)lines, nnodes, moni_str_build);
	reg_refresh_nout(r);
	proc_refcount_dec(proc);

	/*
	 * Dispaly hint message for window type "WIN_TYPE_MONIPROC"
	 */
	r = &dyn->hint;
	reg_erase(r);
	reg_line_write(r, r->nlines_scr - 2, ALIGN_LEFT,
	    "CPU%% = per-node CPU utilization");
	reg_refresh_nout(r);

	return (B_TRUE);
}

/*
 * Display window on screen.
 * (window type: "WIN_TYPE_MONIPROC")
 */
static boolean_t
moniproc_win_draw(dyn_win_t *win)
{
	boolean_t note_out, ret;
	char *p;

	win_title_show();
	ret = moniproc_data_show(win, &note_out);

	if (!note_out) {
		p = (g_cmt_enabled)? NOTE_MONIPROC_LLC : NOTE_MONIPROC;
		win_note_show(p);
	}

	reg_update_all();
	return (ret);
}

/*
 * Convert the perf data to the required format and copy
 * the converted result out via "line".
 * (window type: "WIN_TYPE_MONILWP")
 */
/* ARGSUSED */
static void
monilwp_data_save(track_lwp_t *lwp, int nid_idx,
	int nnodes __attribute__((unused)),
	moni_line_t *line)
{
	node_t *node;
	int intval;

	(void) memset(line, 0, sizeof (moni_line_t));
	if ((node = node_valid_get(nid_idx)) == NULL) {
		return;
	}

	line->nid = node->nid;
	intval = lwp_intval_get(lwp);

	(void) win_countvalue_fill(&line->value, lwp->countval_arr,
	    node->nid, intval, 1);
}

void
win_invalid_lwp(void)
{
	win_warn_msg(WARN_INVALID_LWPID);
	win_note_show(NOTE_INVALID_LWPID);
	(void) sleep(GO_HOME_WAIT);
	disp_go_home();
}

static boolean_t
monilwp_data_show(dyn_win_t *win, boolean_t *note_out)
{
	dyn_monilwp_t *dyn;
	win_reg_t *r;
	char content[WIN_LINECHAR_MAX], intval_buf[16];
	pid_t pid;
	id_t lwpid;
	track_proc_t *proc;
	track_lwp_t *lwp;
	int i, nnodes;
	moni_line_t *lines;

	dyn = (dyn_monilwp_t *)(win->dyn);
	pid = dyn->pid;
	lwpid = dyn->lwpid;

	*note_out = B_FALSE;
	if ((proc = proc_find(pid)) == NULL) {
		win_invalid_proc();
		*note_out = B_TRUE;
		return (B_FALSE);
	}

	if ((lwp = proc_lwp_find(proc, lwpid)) == NULL) {
		proc_refcount_dec(proc);
		win_invalid_lwp();
		*note_out = B_TRUE;
		return (B_FALSE);
	}

	r = &dyn->msg;
	disp_intval(intval_buf, 16);
	(void) snprintf(content, sizeof (content),
	    "Monitoring the thread %d in \"%s\" (interval: %s)",
	    lwpid, proc->name, intval_buf);

	reg_erase(r);
	reg_line_write(r, 1, ALIGN_LEFT, content);
	dump_write("\n*** %s\n", content);
	reg_refresh_nout(r);

	/*
	 * Display the caption of table:
	 * "NODE RMA(K) LMA(K) RMA/LMA CPI CPU%
	 */
	moni_caption_build(content, sizeof (content));
	r = &dyn->caption_cur;
	reg_erase(r);
	reg_line_write(r, 1, ALIGN_LEFT, content);
	dump_write("%s\n", content);
	reg_refresh_nout(r);

	nnodes = node_num();
	r = &dyn->data_cur;
	lines = (moni_line_t *)(r->buf);
	r->nlines_total = nnodes;
	reg_erase(r);

	/*
	 * Save the per-node data with metrics of a specified thread
	 * in scrolling buffer.
	 */
	for (i = 0; i < nnodes; i++) {
		monilwp_data_save(lwp, i, nnodes, &lines[i]);
	}

	/*
	 * Display the per-node data with metrics of a specified thread
	 * in scrolling buffer
	 */
	reg_scroll_show(r, (void *)lines, nnodes, moni_str_build);
	reg_refresh_nout(r);
	lwp_refcount_dec(lwp);
	proc_refcount_dec(proc);

	/*
	 * Dispaly hint message for window type "WIN_TYPE_MONILWP"
	 */
	r = &dyn->hint;
	reg_erase(r);
	reg_line_write(r, r->nlines_scr - 2, ALIGN_LEFT,
	    "CPU%% = per-CPU CPU utilization");
	reg_refresh_nout(r);

	return (B_TRUE);
}

/*
 * Display window on screen.
 * (window type: "WIN_TYPE_MONILWP")
 */
static boolean_t
monilwp_win_draw(dyn_win_t *win)
{
	boolean_t note_out, ret;
	char *p;

	win_title_show();
	ret = monilwp_data_show(win, &note_out);

	if (!note_out) {
		p = (g_cmt_enabled)? NOTE_MONILWP_LLC : NOTE_MONILWP;
		win_note_show(p);
	}

	reg_update_all();
	return (ret);
}

/*
 * The common interface of initializing the screen layout for
 * window type "WIN_TYPE_MONIPROC" and "WIN_TYPE_MONILWP"
 */
static void *
moni_dyn_create(page_t *page, boolean_t (**draw)(dyn_win_t *), win_type_t *type)
{
	void *dyn;

	if (CMD_MONITOR(&page->cmd)->lwpid == 0) {
		if ((dyn = moniproc_dyn_create(
		    CMD_MONITOR(&page->cmd)->pid)) != NULL) {
			*draw = moniproc_win_draw;
			*type = WIN_TYPE_MONIPROC;
			return (dyn);
		}
	} else if ((dyn = monilwp_dyn_create(
	    CMD_MONITOR(&page->cmd)->pid,
	    CMD_MONITOR(&page->cmd)->lwpid)) != NULL) {
		*draw = monilwp_win_draw;
		*type = WIN_TYPE_MONILWP;
		return (dyn);
	}

	return (NULL);
}

/*
 * Release the resources of window.
 * (window type: "WIN_TYPE_MONIPROC")
 */
static void
moniproc_win_destroy(dyn_win_t *win)
{
	dyn_moniproc_t *dyn;

	if ((dyn = win->dyn) != NULL) {
		if (dyn->data_cur.buf != NULL) {
			free(dyn->data_cur.buf);
		}

		reg_win_destroy(&dyn->msg);
		reg_win_destroy(&dyn->caption_cur);
		reg_win_destroy(&dyn->data_cur);
		reg_win_destroy(&dyn->hint);
		free(dyn);
	}
}

/*
 * The function would be called when user hits the <UP>/<DOWN> key
 * to scroll data line.
 * (window type: "WIN_TYPE_MONIPROC")
 */
static void
moniproc_win_scroll(dyn_win_t *win, int scroll_type)
{
	dyn_moniproc_t *dyn = (dyn_moniproc_t *)(win->dyn);

	reg_line_scroll(&dyn->data_cur, scroll_type);
}

/*
 * The function would be called when user hits the "ENTER" key
 * on selected data line.
 * (window type: "WIN_TYPE_MONIPROC")
 */
static void
moniproc_win_scrollenter(dyn_win_t *win)
{
	dyn_moniproc_t *dyn = (dyn_moniproc_t *)(win->dyn);
	win_reg_t *r = &dyn->data_cur;
	scroll_line_t *scroll = &r->scroll;
	moni_line_t *lines;
	cmd_lwp_t cmd_lwp;
	boolean_t badcmd;

	if (scroll->highlight == -1) {
		return;
	}

	/*
	 * Construct a command to switch to next window
	 * "WIN_TYPE_TOPNLWP".
	 */
	lines = (moni_line_t *)(r->buf);
	cmd_lwp.id = CMD_LWP_ID;
	cmd_lwp.pid = lines[scroll->highlight].pid;

	/* LINTED E_BAD_PTR_CAST_ALIGN */
	cmd_execute((cmd_t *)(&cmd_lwp), &badcmd);
}

/*
 * Release the resources for window type "WIN_TYPE_MONILWP"
 */
static void
monilwp_win_destroy(dyn_win_t *win)
{
	dyn_monilwp_t *dyn;

	if ((dyn = win->dyn) != NULL) {
		if (dyn->data_cur.buf != NULL) {
			free(dyn->data_cur.buf);
		}

		reg_win_destroy(&dyn->msg);
		reg_win_destroy(&dyn->caption_cur);
		reg_win_destroy(&dyn->data_cur);
		reg_win_destroy(&dyn->hint);
		free(dyn);
	}
}

/*
 * The function would be called when user hits the <UP>/<DOWN> key
 * to scroll data line.
 * (window type: "WIN_TYPE_MONILWP")
 */
static void
monilwp_win_scroll(dyn_win_t *win, int scroll_type)
{
	dyn_monilwp_t *dyn = (dyn_monilwp_t *)(win->dyn);

	reg_line_scroll(&dyn->data_cur, scroll_type);
}

/*
 * Build the readable string for caption line.
 * (window type: "WIN_TYPE_TOPNLWP")
 */
static void
topnlwp_caption_build(char *buf, int size)
{
	(void) snprintf(buf, size,
	    "%6s%10s%10s%11s%11s%10s%10s%10s",
	    CAPTION_LWP, CAPTION_RPI, CAPTION_LPI,
	    CAPTION_RMA, CAPTION_LMA, CAPTION_RL,
	    CAPTION_CPI, CAPTION_CPU);
}

static void
topnlwp_data_build(char *buf, int size, topnlwp_line_t *line)
{
	char tmp[32];
	win_countvalue_t *value = &line->value;

	(void) snprintf(tmp, sizeof (tmp), "%d", line->lwpid);

	(void) snprintf(buf, size,
	    "%6s%10.1f%10.1f%11.1f%11.1f%10.1f%10.2f%9.1f",
	    tmp, value->rpi, value->lpi, value->rma,
	    value->lma, value->rl, value->cpi, value->cpu * 100);
}

/*
 * Build the readable string for data line.
 * (window type: "WIN_TYPE_TOPNLWP")
 */
static void
topnlwp_str_build(char *buf, int size, int idx, void *pv)
{
	topnlwp_line_t *lines = (topnlwp_line_t *)pv;
	topnlwp_line_t *line = &lines[idx];

	topnlwp_data_build(buf, size, line);
}

/*
 * Build the readable string for scrolling line.
 * (window type: "WIN_TYPE_TOPNLWP")
 */
static void
topnlwp_line_get(win_reg_t *r, int idx, char *line, int size)
{
	topnlwp_line_t *lines;

	lines = (topnlwp_line_t *)(r->buf);
	topnlwp_str_build(line, size, idx, (void *)lines);
}

/*
 * Initialize the display layout.
 * (window type: "WIN_TYPE_TOPNLWP")
 */
static dyn_topnlwp_t *
topnlwp_dyn_create(page_t *page)
{
	dyn_topnlwp_t *dyn;
	void *buf;
	int i;
	cmd_lwp_t *cmd_lwp = (cmd_lwp_t *)(&page->cmd);
	pid_t pid = cmd_lwp->pid;

	if ((buf = zalloc(sizeof (topnlwp_line_t) * WIN_NLINES_MAX)) == NULL) {
		return (NULL);
	}
	if ((dyn = zalloc(sizeof (dyn_topnlwp_t))) == NULL) {
		free(buf);
		return (NULL);
	}

	dyn->pid = pid;

	if ((i = reg_init(&dyn->msg, 0, 1, g_scr_width, 2, A_BOLD)) < 0)
		goto L_EXIT;
	if ((i = reg_init(&dyn->caption, 0, i, g_scr_width, 2, A_BOLD | A_UNDERLINE)) < 0)
		goto L_EXIT;
	if ((i = reg_init(&dyn->data, 0, i, g_scr_width, g_scr_height - i - 4, 0)) < 0)
		goto L_EXIT;

	reg_buf_init(&dyn->data, buf, topnlwp_line_get);
	reg_scroll_init(&dyn->data, B_TRUE);

	(void) reg_init(&dyn->hint, 0, i, g_scr_width,
	    g_scr_height - i - 1, A_BOLD);
	return (dyn);
L_EXIT:
	free(dyn);
	free(buf);
	return (NULL);
}

/*
 * Release the resources of window.
 * (window type: "WIN_TYPE_TOPNLWP")
 */
static void
topnlwp_win_destroy(dyn_win_t *win)
{
	dyn_topnlwp_t *dyn;

	if ((dyn = win->dyn) != NULL) {
		if (dyn->data.buf != NULL) {
			free(dyn->data.buf);
		}

		reg_win_destroy(&dyn->msg);
		reg_win_destroy(&dyn->caption);
		reg_win_destroy(&dyn->data);
		reg_win_destroy(&dyn->hint);
		free(dyn);
	}
}

/*
 * Convert the perf data to the required format and copy
 * the converted result out via "line".
 * (window type: "WIN_TYPE_TOPNLWP")
 */
static void
topnlwp_data_save(track_lwp_t *lwp, int intval, topnlwp_line_t *line)
{
	(void) memset(line, 0, sizeof (topnlwp_line_t));
	line->pid = lwp->proc->pid;
	line->lwpid = lwp->id;

	(void) win_countvalue_fill(&line->value, lwp->countval_arr,
	    NODE_ALL, intval, 1);
}

static boolean_t
topnlwp_data_show(dyn_win_t *win, boolean_t *note_out)
{
	dyn_topnlwp_t *dyn;
	win_reg_t *r;
	char content[WIN_LINECHAR_MAX], intval_buf[16];
	pid_t pid;
	track_proc_t *proc;
	int i, nlwps;
	topnlwp_line_t *lines;
	int intval;
	track_lwp_t *lwp;

	*note_out = B_FALSE;
	dyn = (dyn_topnlwp_t *)(win->dyn);
	pid = dyn->pid;
	if ((pid == -1) || ((proc = proc_find(pid)) == NULL)) {
		win_invalid_proc();
		*note_out = B_TRUE;
		return (B_FALSE);
	}

	/* Get the number of threads in the same process */
	nlwps = MIN(proc_nlwp(proc), WIN_NLINES_MAX);
	disp_intval(intval_buf, 16);

	(void) snprintf(content, sizeof (content),
	    "Monitoring all threads in \"%s\" (%d) (interval: %s)",
	    proc->name, proc->pid, intval_buf);

	r = &dyn->msg;
	reg_erase(r);
	reg_line_write(r, 1, ALIGN_LEFT, content);
	dump_write("\n*** %s\n", content);
	reg_refresh_nout(r);

	/*
	 * Display the caption of data table:
	 * "PID RPI LPI RMA(K) LMA(K) CPI CPU%"
	 */
	topnlwp_caption_build(content, sizeof (content));
	r = &dyn->caption;
	reg_erase(r);
	reg_line_write(r, 1, ALIGN_LEFT, content);
	dump_write("%s\n", content);
	reg_refresh_nout(r);

	r = &dyn->data;
	reg_erase(r);
	r->nlines_total = nlwps;
	lines = (topnlwp_line_t *)(r->buf);
	(void) pthread_mutex_lock(&proc->mutex);

	/*
	 * Sort the threads by the value of CPU utilization
	 */
	proc_lwp_resort(proc, SORT_KEY_CPU);

	/*
	 * Save the data of threads with metrics in scrolling buffer.
	 */
	for (i = 0; i < nlwps; i++) {
		if ((lwp = lwp_sort_next(proc)) == NULL) {
			break;
		}

		intval = lwp_intval_get(lwp);
		topnlwp_data_save(lwp, intval, &lines[i]);
	}

	/*
	 * Display the threads with metrics in scrolling buffer
	 */
	reg_scroll_show(r, (void *)lines, nlwps, topnlwp_str_build);
	(void) pthread_mutex_unlock(&proc->mutex);
	reg_refresh_nout(r);
	proc_refcount_dec(proc);

	/*
	 * Display hint message for window type "WIN_TYPE_TOPNLWP"
	 */
	r = &dyn->hint;
	reg_erase(r);
	reg_line_write(r, r->nlines_scr - 2, ALIGN_LEFT,
	    "CPU%% = per-CPU CPU utilization");
	reg_refresh_nout(r);

	return (B_TRUE);
}

/*
 * Display window on screen.
 * (window type: "WIN_TYPE_TOPNLWP")
 */
static boolean_t
topnlwp_win_draw(dyn_win_t *win)
{
	boolean_t note_out, ret;

	win_title_show();
	ret = topnlwp_data_show(win, &note_out);
	if (!note_out) {
		win_note_show(NOTE_TOPNLWP);
	}

	reg_update_all();
	return (ret);
}

/*
 * The function would be called when user hits the <UP>/<DOWN> key
 * to scroll data line.
 * (window type: "WIN_TYPE_TOPNLWP")
 */
static void
topnlwp_win_scroll(dyn_win_t *win, int scroll_type)
{
	dyn_topnlwp_t *dyn = (dyn_topnlwp_t *)(win->dyn);

	reg_line_scroll(&dyn->data, scroll_type);
}

/*
 * The function would be called when user hits the "ENTER" key
 * on selected data line.
 * (window type: "WIN_TYPE_TOPNLWP")
 */
static void
topnlwp_win_scrollenter(dyn_win_t *win)
{
	dyn_topnlwp_t *dyn = (dyn_topnlwp_t *)(win->dyn);
	win_reg_t *r = &dyn->data;
	scroll_line_t *scroll = &r->scroll;
	topnlwp_line_t *lines;
	cmd_monitor_t cmd_monitor;
	boolean_t badcmd;

	if (scroll->highlight == -1) {
		return;
	}

	/*
	 * Construct a command to switch to next window
	 * (WIN_TYPE_MONILWP)
	 */
	lines = (topnlwp_line_t *)(r->buf);
	cmd_monitor.id = CMD_MONITOR_ID;
	cmd_monitor.pid = lines[scroll->highlight].pid;
	cmd_monitor.lwpid = lines[scroll->highlight].lwpid;

	/* LINTED E_BAD_PTR_CAST_ALIGN */
	cmd_execute((cmd_t *)(&cmd_monitor), &badcmd);
}

/*
 * Build the readable string for data line.
 * (window type: "WIN_TYPE_NODE_OVERVIEW")
 */
static void
nodeoverview_str_build(char *buf, int size, int idx, void *pv)
{
	nodeoverview_line_t *lines = (nodeoverview_line_t *)pv;
	nodeoverview_line_t *line = &lines[idx];
	node_t *node;

	if ((node = node_valid_get(idx)) != NULL) {
		os_nodeoverview_data_build(buf, size, line, node);
	}
}

/*
 * Build the readable string for scrolling line.
 * (window type: "WIN_TYPE_NODE_OVERVIEW")
 */
static void
nodeoverview_line_get(win_reg_t *r, int idx, char *line, int size)
{
	nodeoverview_line_t *lines;

	lines = (nodeoverview_line_t *)(r->buf);
	nodeoverview_str_build(line, size, idx, (void *)lines);
}

/*
 * Initialize the display layout for window type
 * "WIN_TYPE_NODE_OVERVIEW"
 */
static dyn_nodeoverview_t *
nodeoverview_dyn_create(void)
{
	dyn_nodeoverview_t *dyn;
	void *buf_cur;
	int i, nnodes;

	nnodes = node_num();
	if ((buf_cur = zalloc(sizeof (nodeoverview_line_t) * nnodes)) == NULL) {
		return (NULL);
	}
	if ((dyn = zalloc(sizeof (dyn_nodeoverview_t))) == NULL) {
		free(buf_cur);
		return (NULL);
	}

	if ((i = reg_init(&dyn->msg, 0, 1, g_scr_width, 2, A_BOLD)) < 0)
		goto L_EXIT;
	if ((i = reg_init(&dyn->caption_cur, 0, i, g_scr_width, 2,
	    A_BOLD | A_UNDERLINE)) < 0)
		goto L_EXIT;
	if ((i = reg_init(&dyn->data_cur, 0, i, g_scr_width, nnodes, 0)) < 0)
		goto L_EXIT;

	reg_buf_init(&dyn->data_cur, buf_cur, nodeoverview_line_get);
	reg_scroll_init(&dyn->data_cur, B_TRUE);

	(void) reg_init(&dyn->hint, 0, i, g_scr_width,
	    g_scr_height - i - 1, A_BOLD);
	return (dyn);
L_EXIT:
	free(dyn);
	free(buf_cur);
	return (NULL);
}

/*
 * Release the resources of window.
 * (window type: "WIN_TYPE_NODE_OVERVIEW")
 */
static void
nodeoverview_win_destroy(dyn_win_t *win)
{
	dyn_nodeoverview_t *dyn;

	if ((dyn = win->dyn) != NULL) {
		if (dyn->data_cur.buf != NULL) {
			free(dyn->data_cur.buf);
		}

		reg_win_destroy(&dyn->msg);
		reg_win_destroy(&dyn->caption_cur);
		reg_win_destroy(&dyn->data_cur);
		reg_win_destroy(&dyn->hint);
		free(dyn);
	}
}

void
win_node_countvalue(node_t *node, win_countvalue_t *cv)
{
	double d;
	uint64_t rma, lma, clk, ir, all_clks;

	rma = node_countval_get(node, UI_COUNT_RMA);
	lma = node_countval_get(node, UI_COUNT_LMA);
	clk = node_countval_get(node, UI_COUNT_CLK);
	ir = node_countval_get(node, UI_COUNT_IR);

	cv->rpi = ratio(rma * 1000, ir);
	cv->lpi = ratio(lma * 1000, ir);
	cv->cpi = ratio(clk, ir);
	cv->rma = ratio(rma, 1000);
	cv->lma = ratio(lma, 1000);
	cv->rl = ratio(rma, lma);

	d = (double)node_intval_get() / MS_SEC;
	all_clks = (uint64_t)(d * (double)g_clkofsec *
	    (double)node_ncpus(node));
	cv->cpu = ratio(clk, all_clks);
}

/*
 * Convert the perf data to the required format and copy
 * the converted result out via "line".
 * (window type: "WIN_TYPE_MONILWP")
 */
/* ARGSUSED */
static void
nodeoverview_data_save(int nid_idx,
	int nnodes __attribute__((unused)),
	nodeoverview_line_t *line)
{
	node_t *node;
	node_meminfo_t meminfo;

	(void) memset(line, 0, sizeof (nodeoverview_line_t));
	if ((node = node_valid_get(nid_idx)) == NULL) {
		return;
	}

	node_meminfo(node->nid, &meminfo);

	win_node_countvalue(node, &line->value);
	line->nid = node->nid;
	line->mem_all = (double)((double)(meminfo.mem_total) /
	    (double)(GB_BYTES));
	line->mem_free = (double)((double)(meminfo.mem_free) /
	    (double)(GB_BYTES));
}

static boolean_t
nodeoverview_data_show(dyn_win_t *win, boolean_t *note_out)
{
	dyn_nodeoverview_t *dyn;
	win_reg_t *r;
	char content[WIN_LINECHAR_MAX], intval_buf[16];
	int i, nnodes;
	nodeoverview_line_t *lines;

	*note_out = B_FALSE;
	dyn = (dyn_nodeoverview_t *)(win->dyn);

	disp_intval(intval_buf, 16);
	(void) snprintf(content, sizeof (content),
	    "Node Overview (interval: %s)", intval_buf);

	r = &dyn->msg;
	reg_erase(r);
	reg_line_write(r, 1, ALIGN_LEFT, content);
	reg_refresh_nout(r);
	dump_write("\n*** %s\n", content);

	/*
	 * Display the caption of table:
	 */
	os_nodeoverview_caption_build(content, sizeof (content));
	r = &dyn->caption_cur;
	reg_erase(r);
	reg_line_write(r, 1, ALIGN_LEFT, content);
	dump_write("%s\n", content);
	reg_refresh_nout(r);

	nnodes = node_num();
	r = &dyn->data_cur;
	reg_erase(r);
	lines = (nodeoverview_line_t *)(r->buf);
	r->nlines_total = nnodes;

	/*
	 * Save the per-node data with metrics in scrolling buffer.
	 */
	for (i = 0; i < nnodes; i++) {
		nodeoverview_data_save(i, nnodes, &lines[i]);
	}

	/*
	 * Display the per-node data in scrolling buffer
	 */
	reg_scroll_show(r, (void *)lines, nnodes, nodeoverview_str_build);
	reg_refresh_nout(r);

	/*
	 * Dispaly hint message for window type "WIN_TYPE_NODE_OVERVIEW"
	 */
	r = &dyn->hint;
	reg_erase(r);
	reg_line_write(r, r->nlines_scr - 2, ALIGN_LEFT,
	    "CPU%% = per-node CPU utilization");
	reg_refresh_nout(r);

	return (B_TRUE);
}

/*
 * Display window on screen.
 * (window type: "WIN_TYPE_NODE_OVERVIEW")
 */
static boolean_t
nodeoverview_win_draw(dyn_win_t *win)
{
	boolean_t note_out, ret;

	win_title_show();
	node_group_lock();
	ret = nodeoverview_data_show(win, &note_out);
	node_group_unlock();

	if (!note_out) {
		win_note_show(NOTE_NODEOVERVIEW);
	}

	reg_update_all();
	return (ret);
}

/*
 * The function would be called when user hits the <UP>/<DOWN> key
 * to scroll data line.
 * (window type: "WIN_TYPE_NODE_OVERVIEW")
 */
static void
nodeoverview_win_scroll(dyn_win_t *win, int scroll_type)
{
	dyn_nodeoverview_t *dyn = (dyn_nodeoverview_t *)(win->dyn);

	reg_line_scroll(&dyn->data_cur, scroll_type);
}

/*
 * The function would be called when user hits the "ENTER" key
 * on selected data line.
 * (window type: "WIN_TYPE_NODE_OVERVIEW")
 */
static void
nodeoverview_win_scrollenter(dyn_win_t *win)
{
	dyn_nodeoverview_t *dyn = (dyn_nodeoverview_t *)(win->dyn);
	win_reg_t *r = &dyn->data_cur;
	scroll_line_t *scroll = &r->scroll;
	nodeoverview_line_t *lines;
	cmd_node_detail_t cmd;
	boolean_t badcmd;

	if (scroll->highlight == -1) {
		return;
	}

	/*
	 * Construct a command to switch to next window
	 * "WIN_TYPE_NODE_DETAIL".
	 */
	lines = (nodeoverview_line_t *)(r->buf);
	cmd.id = CMD_NODE_DETAIL_ID;
	cmd.nid = lines[scroll->highlight].nid;

	/* LINTED E_BAD_PTR_CAST_ALIGN */
	cmd_execute((cmd_t *)(&cmd), &badcmd);
}

/*
 * Initialize the display layout for window type
 * "WIN_TYPE_NODE_DETAIL"
 */
static dyn_nodedetail_t *
nodedetail_dyn_create(page_t *page)
{
	dyn_nodedetail_t *dyn;
	node_t *node;
	int i;

	if ((dyn = zalloc(sizeof (dyn_nodedetail_t))) == NULL) {
		return (NULL);
	}

	dyn->nid = CMD_NODE_DETAIL(&page->cmd)->nid;
	node = node_get(dyn->nid);
	if (!NODE_VALID(node)) {
		win_warn_msg(WARN_INVALID_NID);
		goto L_EXIT;
	}

	if ((i = reg_init(&dyn->msg, 0, 1, g_scr_width, 2, A_BOLD | A_UNDERLINE)) < 0)
		goto L_EXIT;
	if ((i = reg_init(&dyn->node_data, 0, i, g_scr_width,
	    g_scr_height - i - 4, 0)) < 0)
		goto L_EXIT;
	(void) reg_init(&dyn->hint, 0, i, g_scr_width, 3, A_BOLD);
	return (dyn);
L_EXIT:
	free(dyn);
	return NULL;
}

static boolean_t
nodedetail_data_show(dyn_win_t *win, boolean_t *note_out)
{
	dyn_nodedetail_t *dyn;
	win_reg_t *r;
	char content[WIN_LINECHAR_MAX], intval_buf[16];

	*note_out = B_FALSE;
	dyn = (dyn_nodedetail_t *)(win->dyn);

	/*
	 * Convert the sampling interval (nanosecond) to
	 * a human readable string.
	 */
	disp_intval(intval_buf, 16);
	(void) snprintf(content, sizeof (content),
	    "Node%d information (interval: %s)", dyn->nid, intval_buf);

	r = &dyn->msg;
	reg_erase(r);
	reg_line_write(r, 1, ALIGN_LEFT, content);
	reg_refresh_nout(r);
	dump_write("\n*** %s\n", content);

	os_nodedetail_data((dyn_nodedetail_t *)(win->dyn), &dyn->node_data);

	r = &dyn->hint;
	reg_erase(r);
	reg_line_write(r, 1, ALIGN_LEFT,
	    "CPU%% = per-node CPU utilization");
	reg_refresh_nout(r);

	return (B_FALSE);
}

/*
 * Display window on screen.
 * (window type: "WIN_TYPE_NODE_DETAIL")
 */
static boolean_t
nodedetail_win_draw(dyn_win_t *win)
{
	boolean_t note_out = B_FALSE, ret;

	win_title_show();
	node_group_lock();
	ret = nodedetail_data_show(win, &note_out);
	node_group_unlock();
	if (!note_out) {
		win_note_show(NOTE_NODEDETAIL);
	}

	reg_update_all();
	return (ret);
}

/*
 * Release the resources for window type "WIN_TYPE_NODE_DETAIL"
 */
static void
nodedetail_win_destroy(dyn_win_t *win)
{
	dyn_nodedetail_t *dyn;

	if ((dyn = win->dyn) != NULL) {
		reg_win_destroy(&dyn->msg);
		reg_win_destroy(&dyn->node_data);
		reg_win_destroy(&dyn->hint);
		free(dyn);
	}
}

void
win_callchain_str_build(char *buf, int size, int idx, void *pv)
{
	callchain_line_t *lines = (callchain_line_t *)pv;
	callchain_line_t *line = &lines[idx];

	if (strlen(line->content) > 0) {
		(void) strncpy(buf, line->content, size);
	} else {
		(void) strncpy(buf, " ", size);
	}

	buf[size - 1] = 0;
}

void
win_callchain_line_get(win_reg_t *r, int idx, char *line, int size)
{
	callchain_line_t *lines;

	lines = (callchain_line_t *)(r->buf);
	win_callchain_str_build(line, size, idx, (void *)lines);
}

/*
 * Initialize the display layout.
 * (window type: "WIN_TYPE_CALLCHAIN")
 */
static dyn_callchain_t *
callchain_dyn_create(page_t *page)
{
	dyn_callchain_t *dyn;
	cmd_callchain_t *cmd_callchain = CMD_CALLCHAIN(&page->cmd);
	int i;

	if ((dyn = zalloc(sizeof (dyn_callchain_t))) == NULL) {
		return (NULL);
	}

	dyn->pid = cmd_callchain->pid;
	dyn->lwpid = cmd_callchain->lwpid;
	dyn->ui_countid = UI_COUNT_RMA;

	if ((i = reg_init(&dyn->msg, 0, 1, g_scr_width, 2, A_BOLD)) < 0)
		goto L_EXIT;
	if ((i = reg_init(&dyn->caption, 0, i, g_scr_width, 2, A_BOLD | A_UNDERLINE)) < 0)
		goto L_EXIT;
	if ((i = reg_init(&dyn->pad, 0, i, g_scr_width, 1, 0)) < 0)
		goto L_EXIT;
	if ((i = reg_init(&dyn->data, 0, i, g_scr_width, g_scr_height - i - 4, 0)) < 0)
		goto L_EXIT;
	reg_buf_init(&dyn->data, NULL, win_callchain_line_get);
	reg_scroll_init(&dyn->data, B_TRUE);
	(void) reg_init(&dyn->hint, 0, i, g_scr_width,
	    g_scr_height - i - 1, A_BOLD);
	return (dyn);
L_EXIT:
	free(dyn);
	return (NULL);
}

/*
 * Release the resources of window.
 * (window type: "WIN_TYPE_CALLCHAIN")
 */
static void
callchain_win_destroy(dyn_win_t *win)
{
	dyn_callchain_t *dyn;

	if ((dyn = win->dyn) != NULL) {
		if (dyn->data.buf != NULL) {
			free(dyn->data.buf);
		}

		reg_win_destroy(&dyn->msg);
		reg_win_destroy(&dyn->caption);
		reg_win_destroy(&dyn->pad);
		reg_win_destroy(&dyn->data);
		reg_win_destroy(&dyn->hint);
		free(dyn);
	}
}

static void
callchain_event_name(ui_count_id_t ui_count_id, char *buf, int size)
{
	switch (ui_count_id) {
	case UI_COUNT_RMA:
		(void) strncpy(buf, "RMA", size);
		break;

	case UI_COUNT_CLK:
		(void) strncpy(buf, "Cycle", size);
		break;

	case UI_COUNT_IR:
		(void) strncpy(buf, "IR", size);
		break;

	case UI_COUNT_LMA:
		(void) strncpy(buf, "LMA", size);
		break;

	default:
		(void) strncpy(buf, "-", size);
	}
}

static void
callchain_data_show(dyn_win_t *win, boolean_t *note_out)
{
	dyn_callchain_t *dyn;
	pid_t pid;
	int lwpid;
	track_proc_t *proc;
	track_lwp_t *lwp = NULL;
	win_reg_t *r;
	char content[WIN_LINECHAR_MAX], event_name[32], intval_buf[16];

	dyn = (dyn_callchain_t *)(win->dyn);
	pid = dyn->pid;
	lwpid = dyn->lwpid;
	*note_out = B_FALSE;

	if ((proc = proc_find(pid)) == NULL) {
		win_invalid_proc();
		*note_out = B_TRUE;
		return;
	}

	if ((lwpid > 0) && ((lwp = proc_lwp_find(proc, lwpid)) == NULL)) {
		proc_refcount_dec(proc);
		win_invalid_lwp();
		*note_out = B_TRUE;
		return;
	}

	r = &dyn->msg;
	reg_erase(r);
	callchain_event_name(dyn->ui_countid, event_name, 32);
	disp_intval(intval_buf, 16);
	if (lwpid == 0) {
		(void) snprintf(content, WIN_LINECHAR_MAX,
		    "Call-chain when process generates "
		    "\"%s\" (pid: %d, interval: %s)",
		    event_name, pid, intval_buf);
	} else {
		(void) snprintf(content, WIN_LINECHAR_MAX,
		    "Call-chain when thread generates "
		    "\"%s\" (lwpid: %d, interval: %s)",
		    event_name, lwpid, intval_buf);
	}

	dump_write("\n*** %s\n", content);
	reg_line_write(r, 1, ALIGN_LEFT, content);
	reg_refresh_nout(r);

	/*
	 * Display the call-chain.
	 */
	os_callchain_list_show(dyn, proc, lwp);

	/*
	 * Display hint message for window type "WIN_TYPE_CALLCHAIN"
	 */
	r = &dyn->hint;
	reg_erase(r);
	reg_line_write(r, 1, ALIGN_LEFT,
	    "Switch call-chain by: 1(RMA), 2(LMA), 3(CYCLE), 4(IR)");
	reg_refresh_nout(r);

	if (lwp != NULL) {
		lwp_refcount_dec(lwp);
	}

	proc_refcount_dec(proc);
}

/*
 * Display window on screen.
 * (window type: "WIN_TYPE_CALLCHAIN")
 */
static boolean_t
callchain_win_draw(dyn_win_t *win)
{
	boolean_t note_out;

	win_title_show();
	callchain_data_show(win, &note_out);
	if (!note_out) {
		win_note_show(NOTE_CALLCHAIN);
	}

	reg_update_all();
	return (B_TRUE);
}

/*
 * The callback function for "WIN_TYPE_CALLCHAIN" would be called
 * when user hits the <UP>/<DOWN> key to scroll data line.
 */
static void
callchain_win_scroll(dyn_win_t *win, int scroll_type)
{
	dyn_callchain_t *dyn = (dyn_callchain_t *)(win->dyn);

	reg_line_scroll(&dyn->data, scroll_type);
}

void
win_size2str(uint64_t size, char *buf, int bufsize)
{
	uint64_t i, j;

	/*
	 * "buf" points to a big enough buffer.
	 */
	if ((i = (size / KB_BYTES)) < KB_BYTES) {
		(void) snprintf(buf, bufsize, "%"PRIu64"K", i);
	} else if ((j = i / KB_BYTES) < KB_BYTES) {
		if ((i % KB_BYTES) == 0) {
			(void) snprintf(buf, bufsize, "%"PRIu64"M", j);
		} else {
			(void) snprintf(buf, bufsize, "%.1fM",
			    (double)i / (double)KB_BYTES);
		}
	} else {
		if ((j % KB_BYTES) == 0) {
			(void) snprintf(buf, bufsize, "%"PRIu64"G", j / KB_BYTES);
		} else {
			(void) snprintf(buf, bufsize, "%.1fG",
			    (double)j / (double)KB_BYTES);
		}
	}
}

/*
 * Build the readable string of data line which contains buffer address,
 * buffer size, access%, latency (nanosecond) and buffer description.
 */
void
win_lat_str_build(char *buf, int size, int idx, void *pv)
{
	lat_line_t *lines = (lat_line_t *)pv;
	lat_line_t *line = &lines[idx];
	float hit = 0.0;
	int lat = 0;
	char size_str[32];

	if (line->nsamples > 0) {
		hit = (float)(line->naccess) / (float)(line->nsamples);
	}

	if (line->naccess > 0) {
		lat = (line->latency) / (line->naccess);
	}

	win_size2str(line->bufaddr.size, size_str, sizeof (size_str));

	if (!line->nid_show) {
		(void) snprintf(buf, size, "%16"PRIX64"%8s%10.1f%11"PRIu64"%34s",
		    line->bufaddr.addr, size_str, hit * 100.0, cyc2ns(lat),
		    line->desc);
	} else {
		if (line->nid < 0) {
			(void) snprintf(buf, size, "%16"PRIX64"%8s%8s%10.1f%11"PRIu64,
			    line->bufaddr.addr, size_str, "-", hit * 100.0,
			    cyc2ns(lat));
		} else {
			(void) snprintf(buf, size, "%16"PRIX64"%8s%8d%10.1f%11"PRIu64,
			    line->bufaddr.addr, size_str, line->nid,
			    hit * 100.0, cyc2ns(lat));
		}
	}
}

static void
lat_line_get(win_reg_t *r, int idx, char *line, int size)
{
	lat_line_t *lines;

	lines = (lat_line_t *)(r->buf);
	win_lat_str_build(line, size, idx, (void *)lines);
}

/*
 * Initialize the display layout for window type
 * "WIN_TYPE_LAT_PROC" and "WIN_TYPE_LAT_LWP"
 */
static void *
lat_dyn_create(page_t *page, win_type_t *type)
{
	dyn_lat_t *dyn;
	cmd_lat_t *cmd_lat = CMD_LAT(&page->cmd);
	int i;

	if ((dyn = zalloc(sizeof (dyn_lat_t))) == NULL) {
		return (NULL);
	}

	if ((i = reg_init(&dyn->msg, 0, 1, g_scr_width, 2, A_BOLD)) < 0)
		goto L_EXIT;
	if ((i = reg_init(&dyn->caption, 0, i, g_scr_width, 2, A_BOLD | A_UNDERLINE)) < 0)
		goto L_EXIT;
	(void) reg_init(&dyn->data, 0, i, g_scr_width, g_scr_height - i - 2, 0);

	reg_buf_init(&dyn->data, NULL, lat_line_get);
	reg_scroll_init(&dyn->data, B_TRUE);

	dyn->pid = cmd_lat->pid;
	if ((dyn->lwpid = cmd_lat->lwpid) != 0) {
		*type = WIN_TYPE_LAT_LWP;
	} else {
		*type = WIN_TYPE_LAT_PROC;
	}

	return (dyn);
L_EXIT:
	free(dyn);
	return (NULL);
}

/*
 * Release the resources for window type
 * "WIN_TYPE_LAT_PROC" and "WIN_TYPE_LAT_LWP"
 */
static void
lat_win_destroy(dyn_win_t *win)
{
	dyn_lat_t *dyn;

	if ((dyn = win->dyn) != NULL) {
		if (dyn->data.buf != NULL) {
			free(dyn->data.buf);
		}

		reg_win_destroy(&dyn->msg);
		reg_win_destroy(&dyn->caption);
		reg_win_destroy(&dyn->data);
		free(dyn);
	}
}

/*
 * Due to the limitation of screen size, the string of path
 * probablly needs to be cut. For example:
 * /export/home/jinyao/ws/numatop-gate/usr/src/cmd/numatop/amd64/numatop
 * probably is cut to:
 * ../usr/src/cmd/numatop/amd64/numatop
 */
static void
bufdesc_cut(char *dst_desc, int dst_size, char *src_desc)
{
	int src_len;
	char *start, *end;

	if ((src_len = strlen(src_desc)) < dst_size) {
		(void) strcpy(dst_desc, src_desc);
		if ((src_len == 0) && (dst_size > 0)) {
			dst_desc[0] = 0;
		}

		return;
	}

	start = src_desc + (src_len - dst_size + 1) + 2;
	end = src_desc + src_len;
	while ((start < end) && (*start != '/')) {
		start++;
	}

	if (start < end) {
		(void) snprintf(dst_desc, dst_size, "..%s", start);
		dst_desc[dst_size - 1] = 0;
	} else {
		dst_desc[0] = 0;
	}
}

/*
 * copyout the maps data to a new buffer.
 */
lat_line_t *
win_lat_buf_create(track_proc_t *proc, int lwpid, int *nlines)
{
	map_proc_t *map = &proc->map;
	map_entry_t *entry;
	lat_line_t *buf;
	int i;

	*nlines = map->nentry_cur;
	if ((buf = zalloc(sizeof (lat_line_t) * (*nlines))) == NULL) {
		return (NULL);
	}

	for (i = 0; i < *nlines; i++) {
		entry = &map->arr[i];
		buf[i].pid = proc->pid;
		buf[i].lwpid = lwpid;
		buf[i].bufaddr.addr = entry->start_addr;
		buf[i].bufaddr.size = entry->end_addr - entry->start_addr;
		buf[i].nid_show = B_FALSE;
		bufdesc_cut(buf[i].desc, WIN_DESCBUF_SIZE, entry->desc);
	}

	return (buf);
}

/*
 * Get the LL sampling data, check if the record hits one buffer in
 * process address space. If so, update the accessing statistics for
 * this buffer.
 */
void
win_lat_buf_fill(lat_line_t *lat_buf, int nlines, track_proc_t *proc,
    track_lwp_t *lwp, int *lat)
{
	perf_llrecgrp_t *grp;
	os_perf_llrec_t *rec;
	uint64_t total_sample = 0, total_lat = 0;
	int i;

	(void) pthread_mutex_lock(&proc->mutex);
	if (lwp == NULL) {
		grp = &proc->llrec_grp;
	} else {
		grp = &lwp->llrec_grp;
	}

	for (i = 0; i < grp->nrec_cur; i++) {
		rec = &grp->rec_arr[i];
		os_lat_buf_hit(lat_buf, nlines, rec, &total_lat, &total_sample);
	}

	(void) pthread_mutex_unlock(&proc->mutex);

	for (i = 0; i < nlines; i++) {
		lat_buf[i].nsamples = total_sample;
	}

	*lat = (total_sample > 0) ? (total_lat / total_sample) : 0;
}

/*
 * The callback function used in qsort() to compare the number of
 * buffer accessing.
 */
int
win_lat_cmp(const void *p1, const void *p2)
{
	const lat_line_t *l1 = (const lat_line_t *)p1;
	const lat_line_t *l2 = (const lat_line_t *)p2;

	if (l1->naccess < l2->naccess) {
		return (1);
	}

	if (l1->naccess > l2->naccess) {
		return (-1);
	}

	return (0);
}

/*
 * Get and display the process/thread latency related information.
 */
static int
lat_data_get(track_proc_t *proc, track_lwp_t *lwp, dyn_lat_t *dyn, int *lat)
{
	lat_line_t *lat_buf;
	int nlines, lwpid = 0;
	char content[WIN_LINECHAR_MAX];

	reg_erase(&dyn->caption);
	reg_refresh_nout(&dyn->caption);
	reg_erase(&dyn->data);
	reg_refresh_nout(&dyn->data);

	if (lwp != NULL) {
		lwpid = lwp->id;
	}

	if ((lat_buf = win_lat_buf_create(proc, lwpid, &nlines)) == NULL) {
		debug_print(NULL, 2, "win_lat_buf_create failed (pid = %d)\n",
		    proc->pid);
		return (-1);
	}

	/*
	 * Fill in the memory access information.
	 */
	win_lat_buf_fill(lat_buf, nlines, proc, lwp, lat);

	/*
	 * Sort the "lat_buf" according to the number of buffer accessing.
	 */
	qsort(lat_buf, nlines, sizeof (lat_line_t), win_lat_cmp);

	/*
	 * Display the caption of data table:
	 * "ADDR SIZE ACCESS% LAT(ns) DESC"
	 */
	(void) snprintf(content, sizeof (content),
	    "%16s%8s%11s%11s%34s",
	    CAPTION_ADDR, CAPTION_SIZE, CAPTION_BUFHIT,
	    CAPTION_AVGLAT, CAPTION_DESC);

	reg_line_write(&dyn->caption, 1, ALIGN_LEFT, content);
	dump_write("%s\n", content);
	reg_refresh_nout(&dyn->caption);

	/*
	 * Save data of buffer statistics in scrolling buffer.
	 */
	dyn->data.nlines_total = nlines;
	if (dyn->data.buf != NULL) {
		free(dyn->data.buf);
	}

	/*
	 * Display the buffer with statistics in scrolling buffer
	 */
	dyn->data.buf = (void *)lat_buf;
	reg_scroll_show(&dyn->data, (void *)(dyn->data.buf),
	    nlines, win_lat_str_build);
	reg_refresh_nout(&dyn->data);

	return (0);
}

boolean_t
win_lat_data_show(track_proc_t *proc, dyn_lat_t *dyn, boolean_t *note_out)
{
	win_reg_t *r;
	int lat;
	track_lwp_t *lwp = NULL;
	char content[WIN_LINECHAR_MAX], intval_buf[16], lat_buf[32];

	*note_out = B_FALSE;

	if (dyn->lwpid != 0) {
		if (((lwp = proc_lwp_find(proc, dyn->lwpid)) == NULL) ||
		    (!os_procfs_lwp_valid(proc->pid, dyn->lwpid))) {
			win_invalid_lwp();
			*note_out = B_TRUE;
			return (B_FALSE);
		}
	}

	dump_cache_enable();
	if (lat_data_get(proc, lwp, dyn, &lat) < 0) {
		strcpy(lat_buf, "unknown");
	} else {
		snprintf(lat_buf, sizeof(lat_buf), "%"PRIu64"ns", cyc2ns(lat));
	}
	dump_cache_disable();


	disp_intval(intval_buf, 16);
	if (lwp == NULL) {
		(void) snprintf(content, sizeof (content),
		    "Monitoring memory areas (pid: %d, "
		    "AVG.LAT: %s, interval: %s)",
		    proc->pid, lat_buf, intval_buf);
	} else {
		(void) snprintf(content, sizeof (content),
		    "Monitoring memory areas (lwpid: %d, "
		    "AVG.LAT: %s, interval: %s)",
		    lwp->id, lat_buf, intval_buf);
	}

	r = &dyn->msg;
	reg_erase(r);
	reg_line_write(r, 1, ALIGN_LEFT, content);
	dump_write("\n*** %s\n", content);
	reg_refresh_nout(r);

	dump_cache_flush();

	if (lwp != NULL) {
		lwp_refcount_dec(lwp);
	}

	return (B_TRUE);
}

/*
 * The callback function for "WIN_TYPE_LAT_PROC" and "WIN_TYPE_LAT_LWP"
 * would be called when user hits the <UP>/<DOWN> key to scroll data line.
 */
static void
lat_win_scroll(dyn_win_t *win, int scroll_type)
{
	dyn_lat_t *dyn = (dyn_lat_t *)(win->dyn);

	reg_line_scroll(&dyn->data, scroll_type);
}

/*
 * The callback function for window type "WIN_TYPE_LAT_PROC"
 * and "WIN_TYPE_LAT_LWP" would be called when user hits the "ENTER" key
 * on selected data line.
 */
static void
lat_win_scrollenter(dyn_win_t *win)
{
	dyn_lat_t *dyn = (dyn_lat_t *)(win->dyn);
	win_reg_t *r = &dyn->data;
	scroll_line_t *scroll = &r->scroll;
	lat_line_t *lines;
	cmd_latnode_t cmd;
	boolean_t badcmd;

	if (scroll->highlight == -1) {
		return;
	}

	/*
	 * Construct a command to switch to next window
	 * "WIN_TYPE_LATNODE_PROC" / "WIN_TYPE_LATNODE_LWP"
	 */
	lines = (lat_line_t *)(r->buf);
	cmd.id = CMD_LATNODE_ID;
	cmd.pid = lines[scroll->highlight].pid;
	cmd.lwpid = lines[scroll->highlight].lwpid;
	cmd.addr = lines[scroll->highlight].bufaddr.addr;
	cmd.size = lines[scroll->highlight].bufaddr.size;

	cmd_execute((cmd_t *)(&cmd), &badcmd);
}

/*
 * The callback function for "WIN_TYPE_LATNODE_PROC" and "WIN_TYPE_LATNODE_LWP"
 * would be called when user hits the <UP>/<DOWN> key to scroll data line.
 */
static void
latnode_win_scroll(dyn_win_t *win, int scroll_type)
{
	dyn_latnode_t *dyn = (dyn_latnode_t *)(win->dyn);

	reg_line_scroll(&dyn->data, scroll_type);
}

/*
 * Initialize the display layout for window type
 * "WIN_TYPE_LATNODE_PROC" and "WIN_TYPE_LATNODE_LWP"
 */
static void *
latnode_dyn_create(page_t *page, win_type_t *type)
{
	dyn_latnode_t *dyn;
	cmd_latnode_t *cmd = CMD_LATNODE(&page->cmd);
	int i;

	if ((dyn = zalloc(sizeof (dyn_latnode_t))) == NULL) {
		return (NULL);
	}

	if ((i = reg_init(&dyn->msg, 0, 1, g_scr_width, 2, A_BOLD)) < 0)
		goto L_EXIT;
	if ((i = reg_init(&dyn->note, 0, i, g_scr_width, 2, A_BOLD)) < 0)
		goto L_EXIT;
	if ((i = reg_init(&dyn->caption, 0, i, g_scr_width, 2, A_BOLD | A_UNDERLINE)) <  0)
		goto L_EXIT;
	(void) reg_init(&dyn->data, 0, i, g_scr_width, g_scr_height - i - 2, 0);

	reg_buf_init(&dyn->data, NULL, lat_line_get);
	reg_scroll_init(&dyn->data, B_TRUE);

	dyn->pid = cmd->pid;
	if ((dyn->lwpid = cmd->lwpid) != 0) {
		*type = WIN_TYPE_LATNODE_LWP;
	} else {
		*type = WIN_TYPE_LATNODE_PROC;
	}

	dyn->addr = cmd->addr;
	dyn->size = cmd->size;
	return (dyn);
L_EXIT:
	free(dyn);
	return (NULL);
}

/*
 * Release the resources for window type
 * "WIN_TYPE_LATNODE_PROC" and "WIN_TYPE_LATNODE_LWP"
 */
static void
latnode_win_destroy(dyn_win_t *win)
{
	dyn_latnode_t *dyn;

	if ((dyn = win->dyn) != NULL) {
		if (dyn->data.buf != NULL) {
			free(dyn->data.buf);
		}

		reg_win_destroy(&dyn->msg);
		reg_win_destroy(&dyn->note);
		reg_win_destroy(&dyn->caption);
		reg_win_destroy(&dyn->data);
		free(dyn);
	}
}

/*
 * Build the readable string for data line.
 * (window type: "WIN_TYPE_ACCDST_PROC" and "WIN_TYPE_ACCDST_LWP")
 */
static void
accdst_str_build(char *buf, int size, int idx, void *pv)
{
	accdst_line_t *lines = (accdst_line_t *)pv;
	accdst_line_t *line = &lines[idx];

	(void) snprintf(buf, size, "%5d%14.1f%15"PRIu64,
	    line->nid, line->access_ratio * 100.0, cyc2ns(line->latency));
}

/*
 * Build the readable string for scrolling line.
 * (window type: "WIN_TYPE_ACCDST_PROC" and "WIN_TYPE_ACCDST_LWP")
 */
static void
accdst_line_get(win_reg_t *r, int idx, char *line, int size)
{
	accdst_line_t *lines;

	lines = (accdst_line_t *)(r->buf);
	accdst_str_build(line, size, idx, (void *)lines);
}

/*
 * The callback function for "WIN_TYPE_ACCDST_PROC" and "WIN_TYPE_ACCDST_LWP"
 * would be called when user hits the <UP>/<DOWN> key to scroll data line.
 */
static void
accdst_win_scroll(dyn_win_t *win, int scroll_type)
{
	dyn_accdst_t *dyn = (dyn_accdst_t *)(win->dyn);

	reg_line_scroll(&dyn->data, scroll_type);
}

/*
 * Initialize the display layout for window type
 * "WIN_TYPE_ACCDST_PROC" and "WIN_TYPE_ACCDST_LWP"
 */
static void *
accdst_dyn_create(page_t *page, win_type_t *type)
{
	dyn_accdst_t *dyn;
	void *buf;
	int i, nnodes;
	cmd_accdst_t *cmd_accdst = CMD_ACCDST(&page->cmd);

	nnodes = node_num();
	if ((buf = zalloc(sizeof (accdst_line_t) * nnodes)) == NULL) {
		return (NULL);
	}
	if ((dyn = zalloc(sizeof (dyn_accdst_t))) == NULL) {
		free(buf);
		return (NULL);
	}

	if ((i = reg_init(&dyn->msg, 0, 1, g_scr_width, 2, A_BOLD)) < 0)
		goto L_EXIT;
	if ((i = reg_init(&dyn->caption, 0, i, g_scr_width, 2,
	    A_BOLD | A_UNDERLINE)) < 0)
		goto L_EXIT;
	if ((i = reg_init(&dyn->data, 0, i, g_scr_width, nnodes, 0)) < 0)
		goto L_EXIT;
	reg_buf_init(&dyn->data, buf, accdst_line_get);
	reg_scroll_init(&dyn->data, B_TRUE);
	(void) reg_init(&dyn->hint, 0, i, g_scr_width,
	    g_scr_height - i - 1, A_BOLD);

	dyn->pid = cmd_accdst->pid;
	if ((dyn->lwpid = cmd_accdst->lwpid) != 0) {
		*type = WIN_TYPE_ACCDST_LWP;
	} else {
		*type = WIN_TYPE_ACCDST_PROC;
	}

	return (dyn);
L_EXIT:
	free(dyn);
	free(buf);
	return (NULL);
}

/*
 * Release the resources for window type
 * "WIN_TYPE_ACCDST_PROC" and "WIN_TYPE_ACCDST_LWP"
 */
static void
accdst_win_destroy(dyn_win_t *win)
{
	dyn_accdst_t *dyn;

	if ((dyn = win->dyn) != NULL) {
		if (dyn->data.buf != NULL) {
			free(dyn->data.buf);
		}

		reg_win_destroy(&dyn->msg);
		reg_win_destroy(&dyn->caption);
		reg_win_destroy(&dyn->data);
		reg_win_destroy(&dyn->hint);
		free(dyn);
	}
}

static int
llrec2addr(track_proc_t *proc, track_lwp_t *lwp, void ***addr_arr,
	int **lat_arr, int *addr_num)
{
	perf_llrecgrp_t *grp;
	void **addr_buf;
	int *lat_buf;
	int i, ret = -1;

	(void) pthread_mutex_lock(&proc->mutex);

	if (lwp == NULL) {
		grp = &proc->llrec_grp;
	} else {
		grp = &lwp->llrec_grp;
	}

	if (grp->nrec_cur == 0) {
		*addr_arr = NULL;
		*lat_arr = NULL;
		*addr_num = 0;
		ret = 0;
		goto L_EXIT;
	}

	if ((addr_buf = zalloc(sizeof (void *) * grp->nrec_cur)) == NULL) {
		goto L_EXIT;
	}

	if ((lat_buf = zalloc(sizeof (int) * grp->nrec_cur)) == NULL) {
		free(addr_buf);
		goto L_EXIT;
	}

	for (i = 0; i < grp->nrec_cur; i++) {
		addr_buf[i] = (void *)(grp->rec_arr[i].addr);
		lat_buf[i] = grp->rec_arr[i].latency;
	}

	*addr_arr = addr_buf;
	*lat_arr = lat_buf;
	*addr_num = grp->nrec_cur;
	ret = 0;

L_EXIT:
	(void) pthread_mutex_unlock(&proc->mutex);
	return (ret);
}

static void
accdst_data_save(map_nodedst_t *nodedst_arr, int nnodes_max, int naccess_total,
	int nid_idx, accdst_line_t *line)
{
	node_t *node;
	int naccess;

	(void) memset(line, 0, sizeof (accdst_line_t));
	if ((node = node_valid_get(nid_idx)) == NULL) {
		return;
	}

	if ((node->nid < 0) || (node->nid >= nnodes_max)) {
		return;
	}

	line->nid = node->nid;
	naccess = nodedst_arr[node->nid].naccess;

	if (naccess_total > 0) {
		line->access_ratio = (double)naccess / (double)naccess_total;
	}

	if (naccess > 0) {
		line->latency = nodedst_arr[node->nid].total_lat / naccess;
	}
}

static boolean_t
accdst_data_show(track_proc_t *proc, dyn_accdst_t *dyn, boolean_t *note_out)
{
	win_reg_t *r;
	track_lwp_t *lwp = NULL;
	void **addr_arr = NULL;
	int *lat_arr = NULL;
	int addr_num, i, nnodes, naccess_total = 0;
	map_nodedst_t nodedst_arr[NNODES_MAX];
	accdst_line_t *lines;
	char content[WIN_LINECHAR_MAX], intval_buf[16];
	boolean_t ret = B_FALSE;

	*note_out = B_FALSE;

	if ((dyn->lwpid != 0) &&
	    (lwp = proc_lwp_find(proc, dyn->lwpid)) == NULL) {
		win_invalid_lwp();
		*note_out = B_TRUE;
		return (B_FALSE);
	}

	if (llrec2addr(proc, lwp, &addr_arr, &lat_arr, &addr_num) != 0) {
		goto L_EXIT;
	}

	(void) memset(nodedst_arr, 0, sizeof (map_nodedst_t) * NNODES_MAX);
	if (addr_num > 0) {
		if (map_addr2nodedst(proc->pid, addr_arr, lat_arr, addr_num,
		    nodedst_arr, NNODES_MAX, &naccess_total) != 0) {
			goto L_EXIT;
		}
	}

	r = &dyn->msg;
	reg_erase(r);
	disp_intval(intval_buf, 16);

	if (lwp == NULL) {
		(void) snprintf(content, sizeof (content),
		    "Memory access node distribution overview "
		    "(pid: %d, interval: %s)",
		    proc->pid, intval_buf);
	} else {
		(void) snprintf(content, sizeof (content),
		    "Memory access node distribution overview "
		    "(lwpid: %d, interval: %s)",
		    lwp->id, intval_buf);
	}

	reg_line_write(r, 1, ALIGN_LEFT, content);
	dump_write("\n*** %s\n", content);
	reg_refresh_nout(r);

	r = &dyn->caption;
	reg_erase(r);

	/*
	 * Display the caption of table:
	 * "NODE	ACCESS%		LAT(ns)"
	 */
	(void) snprintf(content, sizeof (content), "%5s%15s%15s",
	    CAPTION_NID, CAPTION_BUFHIT, CAPTION_AVGLAT);

	reg_line_write(r, 1, ALIGN_LEFT, content);
	dump_write("%s\n", content);
	reg_refresh_nout(r);

	nnodes = node_num();
	r = &dyn->data;
	reg_erase(r);
	lines = (accdst_line_t *)(r->buf);
	r->nlines_total = nnodes;

	/*
	 * Save the per-node data with metrics in scrolling buffer.
	 */
	for (i = 0; i < nnodes; i++) {
		accdst_data_save(nodedst_arr, NNODES_MAX, naccess_total, i,
		    &lines[i]);
	}

	/*
	 * Display the per-node data in scrolling buffer
	 */
	reg_scroll_show(r, (void *)lines, nnodes, accdst_str_build);
	reg_refresh_nout(r);

	r = &dyn->hint;
	reg_erase(r);
	reg_refresh_nout(r);

L_EXIT:
	if (lwp != NULL) {
		lwp_refcount_dec(lwp);
	}

	if (addr_arr != NULL) {
		free(addr_arr);
	}

	if (lat_arr != NULL) {
		free(lat_arr);
	}

	return (ret);
}

/*
 * The implementation of displaying window on screen for
 * window type "WIN_TYPE_ACCDST_PROC" and "WIN_TYPE_ACCDST_LWP"
 */
static boolean_t
accdst_win_draw(dyn_win_t *win)
{
	dyn_accdst_t *dyn = (dyn_accdst_t *)(win->dyn);
	track_proc_t *proc;
	boolean_t note_out, ret;

	if ((proc = proc_find(dyn->pid)) == NULL) {
		win_invalid_proc();
		return (B_FALSE);
	}

	win_title_show();
	ret = accdst_data_show(proc, dyn, &note_out);
	if (!note_out) {
		win_note_show(NOTE_ACCDST);
	}

	proc_refcount_dec(proc);
	reg_update_all();
	return (ret);
}

static void
pqos_cmt_proc_data_build(char *buf, int size,
	pqos_cmt_proc_line_t *line)
{
	win_countvalue_t *value = &line->value;
	char tmp[32], id[16];

	if (line->lwpid == 0)
		snprintf(id, sizeof(id), "%d", line->pid);
	else
		snprintf(id, sizeof(id), "%d", line->lwpid);

	if (line->fd == INVALID_FD)
		snprintf(tmp, sizeof(tmp), "%s", id);
	else
		snprintf(tmp, sizeof(tmp), "*%s", id);

	snprintf(buf, size,
	    "%6s%15s%11.1f%11.1f%21.1f%10.1f",
	    tmp, line->proc_name,
	    value->rma, value->lma,
	    ratio(line->llc_occupancy,
	        1048576),
	    value->cpu * 100);
}

static void
pqos_cmt_proc_str_build(char *buf, int size, int idx, void *pv)
{
	pqos_cmt_proc_line_t *lines = (pqos_cmt_proc_line_t *)pv;
	pqos_cmt_proc_line_t *line = &lines[idx];

	pqos_cmt_proc_data_build(buf, size, line);
}

static void
pqos_cmt_proc_line_get(win_reg_t *r, int idx, char *line, int size)
{
	pqos_cmt_proc_line_t *lines;

	lines = (pqos_cmt_proc_line_t *)(r->buf);
	pqos_cmt_proc_str_build(line, size, idx, (void *)lines);
}

static void
pqos_cmt_proc_data_save(track_proc_t *proc, track_lwp_t *lwp, int intval,
	pqos_cmt_proc_line_t *line)
{
	(void) memset(line, 0, sizeof (pqos_cmt_proc_line_t));

	(void) strncpy(line->proc_name, proc->name, sizeof (line->proc_name));
	line->proc_name[WIN_PROCNAME_SIZE - 1] = 0;
	line->pid = proc->pid;
	line->nlwp = proc_nlwp(proc);

	if (lwp == NULL) {
		line->llc_occupancy = proc->pqos.occupancy_scaled;
		win_countvalue_fill(&line->value, proc->countval_arr,
			NODE_ALL, intval, g_ncpus);

	} else {
		line->llc_occupancy = lwp->pqos.occupancy_scaled;
		line->lwpid = lwp->id;
		win_countvalue_fill(&line->value, lwp->countval_arr,
			NODE_ALL, intval, g_ncpus);
	}
}

static dyn_pqos_cmt_proc_t *
pqos_cmt_dyn_create_proc(page_t *page __attribute__((unused)),
	pid_t pid, int lwpid, win_type_t *type)
{
	dyn_pqos_cmt_proc_t *dyn;
	void *buf;
	int i;

	if ((buf = zalloc(sizeof (pqos_cmt_proc_line_t) *
	    WIN_NLINES_MAX)) == NULL) {
		return (NULL);
	}
	if ((dyn = zalloc(sizeof (dyn_pqos_cmt_proc_t))) == NULL) {
		free(buf);
		return (NULL);
	}

	if ((i = reg_init(&dyn->summary, 0, 1, g_scr_width, 2, A_BOLD)) < 0)
		goto L_EXIT;
	if ((i = reg_init(&dyn->caption, 0, i, g_scr_width, 2, A_BOLD | A_UNDERLINE)) < 0)
		goto L_EXIT;
	if ((i = reg_init(&dyn->data, 0, i, g_scr_width, g_scr_height - i - 5, 0)) < 0)
		goto L_EXIT;

	reg_buf_init(&dyn->data, buf, pqos_cmt_proc_line_get);

	reg_scroll_init(&dyn->data, B_TRUE);
	(void) reg_init(&dyn->hint, 0, i, g_scr_width,
	    g_scr_height - i - 1, A_BOLD);

	if (pid == 0)
		*type = WIN_TYPE_PQOS_CMT_TOPNPROC;
	else if (lwpid == 0)
		*type = WIN_TYPE_PQOS_CMT_MONIPROC;
	else
		*type = WIN_TYPE_PQOS_CMT_MONILWP;

	dyn->pid = pid;
	dyn->lwpid = lwpid;

	return (dyn);

L_EXIT:
	free(dyn);
	free(buf);
	return (NULL);
}

static void *
pqos_cmt_dyn_create(page_t *page, win_type_t *type)
{
	cmd_pqos_cmt_t *cmd_pqos = CMD_PQOS_CMT(&page->cmd);

	return pqos_cmt_dyn_create_proc(page, cmd_pqos->pid,
		cmd_pqos->lwpid, type);
}

static void
pqos_cmt_caption_build(int lwpid, char *buf, int size)
{
	if (lwpid == 0)
		snprintf(buf, size,
			"%6s%15s%11s%11s%21s%11s",
			CAPTION_PID, CAPTION_PROC, CAPTION_RMA, CAPTION_LMA,
			CAPTION_LLC_OCCUPANCY, CAPTION_CPU);
	else
		snprintf(buf, size,
			"%6s%15s%11s%11s%21s%11s",
			CAPTION_LWP, CAPTION_PROC, CAPTION_RMA, CAPTION_LMA,
			CAPTION_LLC_OCCUPANCY, CAPTION_CPU);
}

static boolean_t
pqos_cmt_data_show(dyn_win_t *win, boolean_t *note_out)
{
	dyn_pqos_cmt_proc_t *dyn;
	win_reg_t *r, *data_reg;
	char content[WIN_LINECHAR_MAX], intval_buf[16];
	int nprocs, nlwps, i;
	track_proc_t *proc;
	track_lwp_t *lwp;
	int intval;
	pqos_cmt_proc_line_t *lines;
	boolean_t mbm_out = B_FALSE;

	dyn = (dyn_pqos_cmt_proc_t *)(win->dyn);
	data_reg = &dyn->data;

	disp_intval(intval_buf, 16);

	if (dyn->pid == 0) {
		proc_lwp_count(&nprocs, &nlwps);
		nprocs = MIN(nprocs, WIN_NLINES_MAX);
		data_reg->nlines_total = nprocs;

		snprintf(content, sizeof (content),
		    "Monitoring processes with LLC occupancy (interval: %s)",
		    intval_buf);
	} else if (dyn->lwpid == 0) {
		nprocs = 1;
		data_reg->nlines_total = 1;

		snprintf(content, sizeof (content),
		    "Monitoring a process with LLC occupancy (interval: %s)",
		    intval_buf);

		mbm_out = B_TRUE;
	} else {
		nprocs = 1;
		data_reg->nlines_total = 1;

		snprintf(content, sizeof (content),
		    "Monitoring a thread with LLC occupancy (interval: %s)",
		    intval_buf);

		mbm_out = B_TRUE;
	}

	r = &dyn->summary;
	reg_erase(r);
	reg_line_write(r, 1, ALIGN_LEFT, content);
	dump_write("\n*** %s\n", content);
	reg_refresh_nout(r);

	/*
	 * Display the caption of table:
	 * "PID PROC CPU% LLC_OCCUPANCY(MB)"
	 */
	pqos_cmt_caption_build(dyn->lwpid, content, sizeof (content));

	r = &dyn->caption;
	reg_erase(r);
	reg_line_write(r, 1, ALIGN_LEFT, content);
	dump_write("%s\n", content);
	reg_refresh_nout(r);

	reg_erase(data_reg);
	lines = (pqos_cmt_proc_line_t *)(data_reg->buf);

	if (dyn->pid == 0) {
		proc_group_lock();
		proc_resort(SORT_KEY_CPU);

		for (i = 0; i < nprocs; i++) {
			if ((proc = proc_sort_next()) == NULL) {
				break;
			}

			intval = proc_intval_get(proc);
			pqos_cmt_proc_data_save(proc, NULL, intval, &lines[i]);
		}

		proc_group_unlock();

	} else if (dyn->lwpid == 0) {
		if ((proc = proc_find(dyn->pid)) == NULL) {
			win_invalid_proc();
			*note_out = B_TRUE;
			return (B_FALSE);
		}

		intval = proc_intval_get(proc);
		pqos_cmt_proc_data_save(proc, NULL, intval, &lines[0]);
		proc_refcount_dec(proc);
	} else {
		if ((proc = proc_find(dyn->pid)) == NULL) {
			win_invalid_proc();
			*note_out = B_TRUE;
			return (B_FALSE);
		}

		if ((lwp = proc_lwp_find(proc, dyn->lwpid)) == NULL) {
			proc_refcount_dec(proc);
			win_invalid_lwp();
			*note_out = B_TRUE;
			return (B_FALSE);
		}

		intval = lwp_intval_get(lwp);
		pqos_cmt_proc_data_save(proc, lwp, intval, &lines[0]);
		lwp_refcount_dec(lwp);
		proc_refcount_dec(proc);
	}

	reg_scroll_show(data_reg, (void *)lines, nprocs,
	    pqos_cmt_proc_str_build);

	reg_refresh_nout(data_reg);

	r = &dyn->hint;
	reg_erase(r);

	if (dyn->pid == 0) {
		snprintf(content, sizeof (content),
		    "-- Track %d active processes's LLC occupancy (marked with *) --",
		    PERF_PQOS_CMT_MAX);
		reg_line_write(r, 1, ALIGN_LEFT, content);
	}

	reg_line_write(r, 2, ALIGN_LEFT,
	    "CPU%% = system CPU utilization");
	reg_refresh_nout(r);

	if (mbm_out) {
		win_note_show(NOTE_PQOS_CMT_MONI);
		*note_out = B_TRUE;
	} else {
		*note_out = B_FALSE;
	}

	return B_TRUE;
}

static boolean_t
pqos_cmt_win_draw(dyn_win_t *win)
{
	boolean_t note_out, ret;

	win_title_show();
	ret = pqos_cmt_data_show(win, &note_out);

	if (!note_out) {
		win_note_show(NOTE_PQOS_CMT_TOPNPROC);
	}

	reg_update_all();
	return (ret);
}

static void
pqos_cmt_win_destroy(dyn_win_t *win)
{
	dyn_pqos_cmt_proc_t *dyn;

	if ((dyn = win->dyn) != NULL) {
		if (dyn->data.buf != NULL) {
			free(dyn->data.buf);
			dyn->data.buf = NULL;
		}

		reg_win_destroy(&dyn->summary);
		reg_win_destroy(&dyn->caption);
		reg_win_destroy(&dyn->data);
		reg_win_destroy(&dyn->hint);
		free(dyn);
	}
}

static void
pqos_cmt_win_scroll(dyn_win_t *win, int scroll_type)
{
	dyn_pqos_cmt_proc_t *dyn = (dyn_pqos_cmt_proc_t *)(win->dyn);

	reg_line_scroll(&dyn->data, scroll_type);
}

static void
pqos_mbm_proc_data_build(char *buf, int size,
	pqos_mbm_proc_line_t *line)
{
	win_countvalue_t *value = &line->value;
	char id[16], total_bw[16], local_bw[16];

	if (line->lwpid == 0)
		snprintf(id, sizeof(id), "%d", line->pid);
	else
		snprintf(id, sizeof(id), "%d", line->lwpid);

	snprintf(total_bw, sizeof(total_bw), "%.1fMB",
		ratio(line->totalbw_scaled, 1048576));

	snprintf(local_bw, sizeof(local_bw), "%.1fMB",
		ratio(line->localbw_scaled, 1048576));

	snprintf(buf, size,
	    "%6s%15s%10.1f%10.1f%14s%14s%9.1f",
	    id, line->proc_name,
	    value->rma, value->lma,
	    total_bw, local_bw,
	    value->cpu * 100);
}

static void
pqos_mbm_proc_str_build(char *buf, int size, int idx, void *pv)
{
	pqos_mbm_proc_line_t *lines = (pqos_mbm_proc_line_t *)pv;
	pqos_mbm_proc_line_t *line = &lines[idx];

	pqos_mbm_proc_data_build(buf, size, line);
}

static void
pqos_mbm_proc_line_get(win_reg_t *r, int idx, char *line, int size)
{
	pqos_mbm_proc_line_t *lines;

	lines = (pqos_mbm_proc_line_t *)(r->buf);
	pqos_mbm_proc_str_build(line, size, idx, (void *)lines);
}

static void
pqos_mbm_proc_data_save(track_proc_t *proc, track_lwp_t *lwp, int intval,
	pqos_mbm_proc_line_t *line)
{
	(void) memset(line, 0, sizeof (pqos_mbm_proc_line_t));

	(void) strncpy(line->proc_name, proc->name, sizeof (line->proc_name));
	line->proc_name[WIN_PROCNAME_SIZE - 1] = 0;
	line->pid = proc->pid;
	line->nlwp = proc_nlwp(proc);

	if (lwp == NULL) {
		line->totalbw_scaled = proc->pqos.totalbw_scaled;
		line->localbw_scaled = proc->pqos.localbw_scaled;
		win_countvalue_fill(&line->value, proc->countval_arr,
			NODE_ALL, intval, g_ncpus);

	} else {
		line->totalbw_scaled = lwp->pqos.totalbw_scaled;
		line->localbw_scaled = lwp->pqos.localbw_scaled;
		win_countvalue_fill(&line->value, lwp->countval_arr,
			NODE_ALL, intval, g_ncpus);
	}
}

static dyn_pqos_mbm_proc_t *
pqos_mbm_dyn_create_proc(page_t *page __attribute__((unused)),
	pid_t pid, int lwpid, win_type_t *type)
{
	dyn_pqos_mbm_proc_t *dyn;
	void *buf;
	int i;

	if ((buf = zalloc(sizeof (pqos_mbm_proc_line_t) *
	    WIN_NLINES_MAX)) == NULL) {
		return (NULL);
	}

	if ((dyn = zalloc(sizeof (dyn_pqos_mbm_proc_t))) == NULL) {
		free(buf);
		return (NULL);
	}

	if ((i = reg_init(&dyn->summary, 0, 1, g_scr_width, 2, A_BOLD)) < 0)
		goto L_EXIT;
	if ((i = reg_init(&dyn->caption, 0, i, g_scr_width, 2, A_BOLD | A_UNDERLINE)) < 0)
		goto L_EXIT;
	if ((i = reg_init(&dyn->data, 0, i, g_scr_width, g_scr_height - i - 5, 0)) < 0)
		goto L_EXIT;

	reg_buf_init(&dyn->data, buf, pqos_mbm_proc_line_get);

	reg_scroll_init(&dyn->data, B_TRUE);
	(void) reg_init(&dyn->hint, 0, i, g_scr_width,
	    g_scr_height - i - 1, A_BOLD);

	if (pid == 0) {
		/* TODO */
	} else if (lwpid == 0)
		*type = WIN_TYPE_PQOS_MBM_MONIPROC;
	else
		*type = WIN_TYPE_PQOS_MBM_MONILWP;

	dyn->pid = pid;
	dyn->lwpid = lwpid;

	return (dyn);

L_EXIT:
	free(dyn);
	free(buf);
	return (NULL);
}

static void *
pqos_mbm_dyn_create(page_t *page, win_type_t *type)
{
	cmd_pqos_mbm_t *cmd_pqos = CMD_PQOS_MBM(&page->cmd);

	return pqos_mbm_dyn_create_proc(page, cmd_pqos->pid,
		cmd_pqos->lwpid, type);
}

static void
pqos_mbm_caption_build(int lwpid, char *buf, int size)
{
	if (lwpid == 0)
		snprintf(buf, size,
			"%6s%15s%10s%10s%14s%14s%10s",
			CAPTION_PID, CAPTION_PROC, CAPTION_RMA, CAPTION_LMA,
			CAPTION_TOTAL_BW, CAPTION_LOCAL_BW, CAPTION_CPU);
	else
		snprintf(buf, size,
			"%6s%15s%10s%10s%14s%14s%10s",
			CAPTION_LWP, CAPTION_PROC, CAPTION_RMA, CAPTION_LMA,
			CAPTION_TOTAL_BW, CAPTION_LOCAL_BW, CAPTION_CPU);
}

static boolean_t
pqos_mbm_data_show(dyn_win_t *win, boolean_t *note_out)
{
	dyn_pqos_mbm_proc_t *dyn;
	win_reg_t *r, *data_reg;
	char content[WIN_LINECHAR_MAX], intval_buf[16];
	int nprocs = 0;
	track_proc_t *proc = NULL;
	track_lwp_t *lwp = NULL;
	int intval;
	pqos_mbm_proc_line_t *lines;

	dyn = (dyn_pqos_mbm_proc_t *)(win->dyn);
	data_reg = &dyn->data;

	disp_intval(intval_buf, 16);

	if (dyn->pid == 0) {
		/* TODO */
	} else if (dyn->lwpid == 0) {
		nprocs = 1;
		data_reg->nlines_total = 1;

		snprintf(content, sizeof (content),
		    "Monitoring a process with memory bandwidth (interval: %s)",
		    intval_buf);
	} else {
		nprocs = 1;
		data_reg->nlines_total = 1;

		snprintf(content, sizeof (content),
		    "Monitoring a thread with memory bandwidth (interval: %s)",
		    intval_buf);
	}

	r = &dyn->summary;
	reg_erase(r);
	reg_line_write(r, 1, ALIGN_LEFT, content);
	dump_write("\n*** %s\n", content);
	reg_refresh_nout(r);

	pqos_mbm_caption_build(dyn->lwpid, content, sizeof (content));

	r = &dyn->caption;
	reg_erase(r);
	reg_line_write(r, 1, ALIGN_LEFT, content);
	dump_write("%s\n", content);
	reg_refresh_nout(r);

	reg_erase(data_reg);
	lines = (pqos_mbm_proc_line_t *)(data_reg->buf);

	if (dyn->pid == 0) {
		/* TODO */
	} else if (dyn->lwpid == 0) {
		if ((proc = proc_find(dyn->pid)) == NULL) {
			win_invalid_proc();
			*note_out = B_TRUE;
			return (B_FALSE);
		}

		intval = proc_intval_get(proc);
		pqos_mbm_proc_data_save(proc, NULL, intval, &lines[0]);
		proc_refcount_dec(proc);
	} else {
		if ((proc = proc_find(dyn->pid)) == NULL) {
			win_invalid_proc();
			*note_out = B_TRUE;
			return (B_FALSE);
		}

		if ((lwp = proc_lwp_find(proc, dyn->lwpid)) == NULL) {
			proc_refcount_dec(proc);
			win_invalid_lwp();
			*note_out = B_TRUE;
			return (B_FALSE);
		}

		intval = lwp_intval_get(lwp);
		pqos_mbm_proc_data_save(proc, lwp, intval, &lines[0]);
		lwp_refcount_dec(lwp);
		proc_refcount_dec(proc);
	}

	reg_scroll_show(data_reg, (void *)lines, nprocs,
	    pqos_mbm_proc_str_build);

	reg_refresh_nout(data_reg);

	r = &dyn->hint;
	reg_erase(r);

	if (dyn->pid == 0) {
		snprintf(content, sizeof (content),
		    "-- Track %d active processes's LLC occupancy (marked with *) --",
		    PERF_PQOS_CMT_MAX);
		reg_line_write(r, 1, ALIGN_LEFT, content);
	}

	reg_line_write(r, 2, ALIGN_LEFT,
	    "CPU%% = system CPU utilization; MBAND = Memory Bandwidth");
	reg_refresh_nout(r);

	*note_out = B_FALSE;

	return B_TRUE;
}

static boolean_t
pqos_mbm_win_draw(dyn_win_t *win)
{
	boolean_t note_out, ret;

	win_title_show();
	ret = pqos_mbm_data_show(win, &note_out);

	if (!note_out) {
		win_note_show(NOTE_PQOS_MBM);
	}

	reg_update_all();
	return (ret);
}

static void
pqos_mbm_win_destroy(dyn_win_t *win)
{
	dyn_pqos_mbm_proc_t *dyn;

	if ((dyn = win->dyn) != NULL) {
		if (dyn->data.buf != NULL) {
			free(dyn->data.buf);
			dyn->data.buf = NULL;
		}

		reg_win_destroy(&dyn->summary);
		reg_win_destroy(&dyn->caption);
		reg_win_destroy(&dyn->data);
		reg_win_destroy(&dyn->hint);
		free(dyn);
	}
}

static void
pqos_mbm_win_scroll(dyn_win_t *win, int scroll_type)
{
	dyn_pqos_mbm_proc_t *dyn = (dyn_pqos_mbm_proc_t *)(win->dyn);

	reg_line_scroll(&dyn->data, scroll_type);
}

/*
 * The common entry for all warning messages.
 */
void
win_warn_msg(warn_type_t warn_type)
{
	dyn_warn_t dyn;
	char content[WIN_LINECHAR_MAX];
	int i;

	if ((i = reg_init(&dyn.msg, 0, 1, g_scr_width, 4, A_BOLD)) < 0)
		return;
	(void) reg_init(&dyn.pad, 0, i, g_scr_width,
	    g_scr_height - i - 2, 0);

	reg_erase(&dyn.pad);
	reg_line_write(&dyn.pad, 0, ALIGN_LEFT, "");
	reg_refresh_nout(&dyn.pad);

	reg_erase(&dyn.msg);
	switch (warn_type) {
	case WARN_PERF_DATA_FAIL:
		(void) strncpy(content, "Perf event counting is failed!",
		    WIN_LINECHAR_MAX);
		break;

	case WARN_INVALID_PID:
		(void) strncpy(content, "Process exists, "
		    "return to home window ...",
		    WIN_LINECHAR_MAX);
		break;

	case WARN_INVALID_LWPID:
		(void) strncpy(content, "Thread exists, "
		    "return to home window ...",
		    WIN_LINECHAR_MAX);
		break;

	case WARN_WAIT:
		(void) strncpy(content, "Please wait ...",
		    WIN_LINECHAR_MAX);
		break;

	case WARN_WAIT_PERF_LL_RESULT:
		(void) strncpy(content, "Retrieving latency data ...",
		    WIN_LINECHAR_MAX);
		break;

	case WARN_NOT_IMPL:
		(void) strncpy(content, "Function is not implemented yet!",
		    WIN_LINECHAR_MAX);
		break;

	case WARN_INVALID_NID:
		(void) strncpy(content, "Invalid node id, node might "
		    "be offlined.",
		    WIN_LINECHAR_MAX);
		break;

	case WARN_INVALID_MAP:
		(void) strncpy(content, "Cannot retrieve process "
		    "address-space mapping.",
		    WIN_LINECHAR_MAX);
		break;

	case WARN_INVALID_NUMAMAP:
		(void) strncpy(content, "Cannot retrieve process "
		    "memory NUMA mapping.",
		    WIN_LINECHAR_MAX);
		break;

	case WARN_LL_NOT_SUPPORT:
		(void) strncpy(content, "Sampling isn't working properly.",
		    WIN_LINECHAR_MAX);
		break;

	case WARN_STOP:
		(void) strncpy(content, "Stopping ...",
		    WIN_LINECHAR_MAX);
		break;

	default:
		content[0] = '\0';
	}

	content[WIN_LINECHAR_MAX - 1] = 0;
	reg_line_write(&dyn.msg, 1, ALIGN_LEFT, content);
	reg_refresh_nout(&dyn.msg);
	reg_update_all();

	reg_win_destroy(&dyn.msg);
	reg_win_destroy(&dyn.pad);
}

/*
 * Each window has same fix regions: the title is at the top of window
 * and the note region is at the bottom of window.
 */
void
win_fix_init(void)
{
	(void) reg_init(&s_note_reg, 0, g_scr_height - 1,
	    g_scr_width, 1, A_REVERSE | A_BOLD);
	(void) reg_init(&s_title_reg, 0, 0, g_scr_width, 1, 0);
	reg_update_all();
}

/*
 * Release the resources of fix regions in window.
 */
void
win_fix_fini(void)
{
	reg_win_destroy(&s_note_reg);
	reg_win_destroy(&s_title_reg);
}

/*
 * The common entry of window initialization
 */
int
win_dyn_init(void *p)
{
	page_t *page = (page_t *)p;
	dyn_win_t *win = &page->dyn_win;
	cmd_id_t cmd_id = CMD_ID(&page->cmd);
	int ret = -1;

	/*
	 * Initialization for the common regions for all windows.
	 */
	win->title = &s_title_reg;
	win->note = &s_note_reg;
	win->page = page;

	/*
	 * Initialization for the private regions according to
	 * different window type.
	 */
	switch (cmd_id) {
	case CMD_IR_NORMALIZE_ID:
		if ((win->dyn = topnproc_dyn_create(
		    WIN_TYPE_TOPNPROC)) == NULL) {
			goto L_EXIT;
		}

		win->type = WIN_TYPE_TOPNPROC;
		win->draw = topnproc_win_draw;
		win->scroll = topnproc_win_scroll;
		win->scroll_enter = topnproc_win_scrollenter;
		win->destroy = topnproc_win_destroy;
		break;

	case CMD_HOME_ID:
		if ((win->dyn = topnproc_dyn_create(
		    WIN_TYPE_RAW_NUM)) == NULL) {
			goto L_EXIT;
		}

		win->type = WIN_TYPE_RAW_NUM;
		win->draw = topnproc_win_draw;
		win->scroll = topnproc_win_scroll;
		win->scroll_enter = topnproc_win_scrollenter;
		win->destroy = topnproc_win_destroy;
		break;

	case CMD_MONITOR_ID:
		if ((win->dyn = moni_dyn_create(
		    page, &win->draw, &win->type)) == NULL) {
			goto L_EXIT;
		}

		if (win->type == WIN_TYPE_MONILWP) {
			win->destroy = monilwp_win_destroy;
			win->scroll = monilwp_win_scroll;
		} else {
			win->destroy = moniproc_win_destroy;
			win->scroll = moniproc_win_scroll;
			win->scroll_enter = moniproc_win_scrollenter;
		}
		break;

	case CMD_LWP_ID:
		if ((win->dyn = topnlwp_dyn_create(page)) == NULL) {
			goto L_EXIT;
		}

		win->type = WIN_TYPE_TOPNLWP;
		win->draw = topnlwp_win_draw;
		win->scroll = topnlwp_win_scroll;
		win->scroll_enter = topnlwp_win_scrollenter;
		win->destroy = topnlwp_win_destroy;
		break;

	case CMD_NODE_OVERVIEW_ID:
		if ((win->dyn = nodeoverview_dyn_create()) == NULL) {
			goto L_EXIT;
		}

		win->type = WIN_TYPE_NODE_OVERVIEW;
		win->draw = nodeoverview_win_draw;
		win->scroll = nodeoverview_win_scroll;
		win->scroll_enter = nodeoverview_win_scrollenter;
		win->destroy = nodeoverview_win_destroy;
		break;

	case CMD_NODE_DETAIL_ID:
		if ((win->dyn = nodedetail_dyn_create(page)) == NULL) {
			goto L_EXIT;
		}

		win->type = WIN_TYPE_NODE_DETAIL;
		win->draw = nodedetail_win_draw;
		win->destroy = nodedetail_win_destroy;
		break;

	case CMD_CALLCHAIN_ID:		
		if ((win->dyn = callchain_dyn_create(page)) == NULL) {
			goto L_EXIT;
		}

		win->type = WIN_TYPE_CALLCHAIN;
		win->draw = callchain_win_draw;
		win->destroy = callchain_win_destroy;
		win->scroll = callchain_win_scroll;
		break;

	case CMD_LAT_ID:
		if ((win->dyn = lat_dyn_create(page, &win->type)) == NULL) {
			goto L_EXIT;
		}

		win->draw = os_lat_win_draw;
		win->destroy = lat_win_destroy;
		win->scroll = lat_win_scroll;
		win->scroll_enter = lat_win_scrollenter;
		break;

	case CMD_LATNODE_ID:
		if ((win->dyn = latnode_dyn_create(page, &win->type)) == NULL) {
			goto L_EXIT;
		}

		win->draw = os_latnode_win_draw;
		win->destroy = latnode_win_destroy;
		win->scroll = latnode_win_scroll;
		break;

	case CMD_ACCDST_ID:
		if ((win->dyn = accdst_dyn_create(page, &win->type)) == NULL) {
			goto L_EXIT;
		}

		win->draw = accdst_win_draw;
		win->destroy = accdst_win_destroy;
		win->scroll = accdst_win_scroll;
		break;
		
	case CMD_LLCALLCHAIN_ID:
		if ((win->dyn = os_llcallchain_dyn_create(page)) == NULL) {
			goto L_EXIT;
		}

		win->type = WIN_TYPE_LLCALLCHAIN;
		win->draw = os_llcallchain_win_draw;
		win->destroy = os_llcallchain_win_destroy;
		win->scroll = os_llcallchain_win_scroll;
		break;

	case CMD_PQOS_CMT_ID:
		if ((win->dyn = pqos_cmt_dyn_create(page, &win->type)) == NULL) {
			goto L_EXIT;
		}

		win->draw = pqos_cmt_win_draw;
		win->destroy = pqos_cmt_win_destroy;
		win->scroll = pqos_cmt_win_scroll;
		break;

	case CMD_PQOS_MBM_ID:
		if ((win->dyn = pqos_mbm_dyn_create(page, &win->type)) == NULL) {
			goto L_EXIT;
		}

		win->draw = pqos_mbm_win_draw;
		win->destroy = pqos_mbm_win_destroy;
		win->scroll = pqos_mbm_win_scroll;
		break;

	default:
		goto L_EXIT;
	}

	win->inited = B_TRUE;
	ret = 0;

L_EXIT:
	return (ret);
}

/*
 * The common entry of window destroying
 */
void
win_dyn_fini(void *p)
{
	page_t *page = (page_t *)p;
	dyn_win_t *win = &page->dyn_win;

	if (win->inited && (win->destroy != NULL)) {
		win->destroy(win);
	}

	win->inited = B_FALSE;
}