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.
 */

#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/win.h"
#include "../include/ui_perf_map.h"
#include "../include/os/node.h"
#include "../include/os/os_perf.h"
#include "../include/os/os_util.h"
#include "../include/os/plat.h"
#include "../include/os/os_win.h"

/*
 * Build the readable string for caption line.
 * (window type: "WIN_TYPE_NODE_OVERVIEW")
 */
void
os_nodeoverview_caption_build(char *buf, int size)
{
	(void) snprintf(buf, size,
	    "%5s%12s%12s%11s%11s%11s%12s",
	    CAPTION_NID, CAPTION_MEM_ALL, CAPTION_MEM_FREE,
	    CAPTION_RMA, CAPTION_LMA, CAPTION_RL, CAPTION_CPU);
}

void
os_nodeoverview_data_build(char *buf, int size, nodeoverview_line_t *line,
    node_t *node)
{
	win_countvalue_t *value = &line->value;
	char mem_all[32], mem_free[32];

	(void) snprintf(mem_all, sizeof (mem_all), "%.1fG", line->mem_all);
	(void) snprintf(mem_free, sizeof (mem_free), "%.1fG", line->mem_free);

	if (plat_offcore_num() > 1) {
		(void) snprintf(buf, size,
		    "%5d%12s%12s%11.1f%11.1f%11.1f%11.1f",
		    node->nid, mem_all, mem_free, value->rma, value->lma,
		    value->rl, value->cpu * 100);
	} else {
		(void) snprintf(buf, size,
		    "%5d%12s%12s%11.1f%11s%11s%11.1f",
		    node->nid, mem_all, mem_free, value->rma,
		    "-", "-", value->cpu * 100);		
	}
}

static int
cpuid_cmp(const void *a, const void *b)
{
	const int *id1 = (const int *)a;
	const int *id2 = (const int *)b;

	if (*id1 > *id2) {
		return (1);
	}

	if (*id1 < *id2) {
		return (-1);
	}

	return (0);
}

/*
 * Build a readable string of CPU ID and try to reduce the string length. e.g.
 * For cpu1, cpu2, cpu3, cpu4, the string is "CPU(1-4)",
 * For cpu1, cpu3, cpu5, cpu7, the string is "CPU(1 3 5 7)"
 */
static void
node_cpu_string(node_t *node, char *s1, int size)
{
	char s2[128], s3[128];
	int i, j, k, l, cpuid_start;
	int *cpuid_arr;
	int ncpus;
	perf_cpu_t *cpus = node_cpus(node);

	s1[0] = 0;
	if ((ncpus = node->ncpus) == 0) {
		(void) strncpy(s1, "-", size);
		return;
	}

	if ((cpuid_arr = zalloc(sizeof (int) * ncpus)) == NULL) {
		return;
	}

	j = 0;
	for (i = 0; (i < NCPUS_NODE_MAX) && (j < ncpus); i++) {
		if ((cpus[i].cpuid != INVALID_CPUID) && (!cpus[i].hotremove)) {
			cpuid_arr[j++] = cpus[i].cpuid;
		}
	}

	qsort(cpuid_arr, ncpus, sizeof (int), cpuid_cmp);
	cpuid_start = cpuid_arr[0];

	if (ncpus == 1) {
		(void) snprintf(s2, sizeof (s2), "%d", cpuid_start);
        	(void) strncat(s1, s2, strlen(s2));
        	free(cpuid_arr);
		return;
	}

	l = 1;
	k = 1;

	for (j = 1; j < ncpus; j++) {
		k++;
		if (cpuid_arr[j] != cpuid_start + l) {
			if (k < ncpus) {
				if (l == 1) {
					(void) snprintf(s2, sizeof (s2), "%d ", cpuid_start);
				} else {
					(void) snprintf(s2, sizeof (s2),
						"%d-%d ", cpuid_start, cpuid_start + l - 1);
				}
          		} else {
				if (l == 1) {
					(void) snprintf(s2, sizeof (s2), "%d",
						cpuid_start);
				} else {
					(void) snprintf(s2, sizeof (s2), "%d-%d",
						cpuid_start, cpuid_start + l - 1);
				}

				(void) snprintf(s3, sizeof (s3), " %d",
					cpuid_arr[j]);
	        	  	(void) strncat(s2, s3, strlen(s3));
			}

          		(void) strncat(s1, s2, strlen(s2));
          		cpuid_start = cpuid_arr[j];
           		l = 1;
		} else {
	        	if (k == ncpus) {
        	    		(void) snprintf(s2, sizeof (s2), "%d-%d",
                			cpuid_start, cpuid_start + l);
         			(void) strncat(s1, s2, strlen(s2));
       			} else {
            			l++;
       			}
       		}
	}

	free(cpuid_arr);
}

static void
nodedetail_line_show(win_reg_t *reg, char *title, char *value, int line)
{
	char s1[256];

	snprintf(s1, sizeof (s1), "%-30s%15s", title, value);
	reg_line_write(reg, line, ALIGN_LEFT, s1);
	dump_write("%s\n", s1);
}

/*
 * Display the performance statistics per node.
 */
void
os_nodedetail_data(dyn_nodedetail_t *dyn, win_reg_t *seg)
{
	char s1[256], s2[32];
	node_t *node;
	win_countvalue_t value;
	node_meminfo_t meminfo;
	int i = 1, j;
	node_qpi_t *qpi;
	node_imc_t *imc;
	uint64_t v = 0;

	reg_erase(seg);
	node = node_get(dyn->nid);
	win_node_countvalue(node, &value);
	node_meminfo(node->nid, &meminfo);

	/*
	 * Display the CPU
	 */
	node_cpu_string(node, s1, sizeof (s1));
	nodedetail_line_show(seg, "CPU:", s1, i++);

	/*
	 * Display the CPU utilization
	 */
	(void) snprintf(s1, sizeof (s1), "%.1f%%", value.cpu * 100.0);	
	nodedetail_line_show(seg, "CPU%:", s1, i++);

	/*
	 * Display the number of RMA
	 */
	(void) snprintf(s1, sizeof (s1), "%.1fK", value.rma);	
	nodedetail_line_show(seg, "RMA:", s1, i++);

	/*
	 * Display the number of LMA if platform supports
	 */
	if (plat_offcore_num() > 1) {
		(void) snprintf(s1, sizeof (s1), "%.1fK", value.lma);	
	} else {
		(void) snprintf(s1, sizeof (s1), "%s", "-");	
	}

	nodedetail_line_show(seg, "LMA:", s1, i++);

	/*
	 * Display the size of total memory
	 */
	(void) snprintf(s1, sizeof (s1), "%.1fG",
		(double)((double)(meminfo.mem_total) / (double)(GB_BYTES)));
	nodedetail_line_show(seg, "MEM total:", s1, i++);

	/*
	 * Display the size of free memory
	 */
	(void) snprintf(s1, sizeof (s1), "%.1fG",
		(double)((double)(meminfo.mem_free) / (double)(GB_BYTES)));
	nodedetail_line_show(seg, "MEM free:", s1, i++);

	/*
	 * Display the size of active memory.
	 */
	(void) snprintf(s1, sizeof (s1), "%.2fG",
		(double)((double)(meminfo.active) / (double)(GB_BYTES)));	
	nodedetail_line_show(seg, "MEM active:", s1, i++);

	/*
	 * Display the size of inactive memory.
	 */
	(void) snprintf(s1, sizeof (s1), "%.2fG",
		(double)((double)(meminfo.inactive) / (double)(GB_BYTES)));
	nodedetail_line_show(seg, "MEM inactive:", s1, i++);

	/*
	 * Display the size of dirty memory.
	 */
	(void) snprintf(s1, sizeof (s1), "%.2fG",
		(double)((double)(meminfo.dirty) / (double)(GB_BYTES)));
	nodedetail_line_show(seg, "Dirty:", s1, i++);

	/*
	 * Display the size of writeback memory.
	 */
	(void) snprintf(s1, sizeof (s1), "%.2fG",
		(double)((double)(meminfo.dirty) / (double)(GB_BYTES)));
	nodedetail_line_show(seg, "Writeback:", s1, i++);

	/*
	 * Display the size of mapped memory.
	 */
	(void) snprintf(s1, sizeof (s1), "%.2fG",
		(double)((double)(meminfo.mapped) / (double)(GB_BYTES)));
	nodedetail_line_show(seg, "Mapped:", s1, i++);

	/*
	 * Display the QPI link bandwidth
	 */
	qpi = &node->qpi;

	for (j = 0; j < qpi->qpi_num; j++) {		
		snprintf(s1, sizeof (s1), "%.1fMB", 
			ratio(qpi->qpi_info[j].value_scaled * 8, 1024 * 1024));
		snprintf(s2, sizeof (s2), "QPI/UPI %d bandwidth:", j);
		nodedetail_line_show(seg, s2, s1, i++);	
	}

	/*
	 * Display the memory controller bandwidth
	 */
	imc = &node->imc;

	for (j = 0; j < imc->imc_num; j++) {
		v += imc->imc_info[j].value_scaled * 64;
	}

	snprintf(s1, sizeof (s1), "%.1fMB", ratio(v, 1024 * 1024));
	nodedetail_line_show(seg,  "Memory controller bandwidth:", s1, i++);	

	reg_refresh_nout(seg);
}

static void
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) {
		strncpy(buf, line->content, size);
	} else {
		strncpy(buf, " ", size);
	}

	buf[size - 1] = 0;
}

static int
chainlist_show(sym_chainlist_t *chainlist, win_reg_t *reg)
{
	sym_callchain_t *chain;
	int nentry, nchain;
	int i, j = 0, k, nlines;
	char content[WIN_LINECHAR_MAX];
	callchain_line_t *buf, *line;

	sym_callchain_resort(chainlist);
	nentry = sym_chainlist_nentry(chainlist, &nchain);

	reg_erase(reg);
	if (nentry == 0) {
		snprintf(content, WIN_LINECHAR_MAX,
			"<- Detecting call-chain ... -> ");
		reg_line_write(reg, 0, ALIGN_LEFT, content);
		dump_write("%s\n", content);
		reg_refresh_nout(reg);
		return (0);
	}
	
	nlines = nentry + 2 * nchain;
	if ((buf = zalloc(nlines * sizeof (callchain_line_t))) == NULL) {
		return (-1);
	}
	
	for (i = 0; i < nchain; i++) {
		if ((chain = sym_callchain_detach(chainlist)) == NULL) {
			break;
		}
		
		line = &buf[j++];
		snprintf(line->content, WIN_LINECHAR_MAX,
			"<- call-chain %d: ->", i + 1);

		for (k = 0; k < chain->nentry; k++) {
			line = &buf[j++];			
			strncpy(line->content, chain->entry_arr[k].name, WIN_LINECHAR_MAX);
			line->content[WIN_LINECHAR_MAX - 1] = 0;
		}

		line = &buf[j++];
		strcpy(line->content, "");
		sym_callchain_free(chain);
	}

	if (reg->buf != NULL) {
		free(reg->buf);
	}

	reg->buf = (void *)buf;
	reg->nlines_total = nlines - 1;
	reg_scroll_show(reg, (void *)(reg->buf), nlines - 1, callchain_str_build);
	reg_refresh_nout(reg);
	sym_chainlist_free(chainlist);
	return (0);
}

int
os_callchain_list_show(dyn_callchain_t *dyn, track_proc_t *proc,
	track_lwp_t *lwp)
{
	perf_countchain_t *count_chain;
	perf_chainrecgrp_t *rec_grp;
	perf_chainrec_t *rec_arr;
	sym_chainlist_t chainlist;
	win_reg_t *reg;
	char content[WIN_LINECHAR_MAX];
	int i, j;
	int n_perf_count;
	perf_count_id_t *perf_count_ids = NULL;

	reg = &dyn->caption;
	reg_erase(reg);	
	snprintf(content, WIN_LINECHAR_MAX, "Call-chain list:");
	reg_line_write(reg, 1, ALIGN_LEFT, content);
	dump_write("%s\n", content);
	reg_refresh_nout(reg);

	reg = &dyn->pad;
	reg_erase(reg);	
	dump_write("\n");
	reg_refresh_nout(reg);

	if (lwp != NULL) {
		count_chain = &lwp->count_chain;
	} else {
		count_chain = &proc->count_chain;
	}

	if (sym_load(proc, SYM_TYPE_FUNC) != 0) {
		debug_print(NULL, 2, "Failed to load the process symbol "
			"(pid = %d)\n", proc->pid);
		return (-1);
	}

	memset(&chainlist, 0, sizeof (sym_chainlist_t));

	n_perf_count = get_ui_perf_count_map(dyn->ui_countid, &perf_count_ids);

	for (i = 0; i < n_perf_count; i++) {
		rec_grp = &count_chain->chaingrps[perf_count_ids[i]];
		rec_arr = rec_grp->rec_arr;

		for (j = 0; j < rec_grp->nrec_cur; j++) {
			sym_callchain_add(&proc->sym, rec_arr[j].callchain.ips,
				rec_arr[j].callchain.ip_num, &chainlist);
		}
	}

	chainlist_show(&chainlist, &dyn->data);
	return (0);
}

/*
 * The implementation of displaying window on screen for
 * window type "WIN_TYPE_LAT_PROC" and "WIN_TYPE_LAT_LWP"
 */
boolean_t
os_lat_win_draw(dyn_win_t *win)
{
	dyn_lat_t *dyn = (dyn_lat_t *)(win->dyn);
	track_proc_t *proc;
	boolean_t note_out, ret;

	if (!perf_ll_started()) {		
		win_warn_msg(WARN_LL_NOT_SUPPORT);
		win_note_show(NOTE_LAT);
		return (B_FALSE);
	}

	if ((proc = proc_find(dyn->pid)) == NULL) {
		win_warn_msg(WARN_INVALID_PID);
		win_note_show(NOTE_INVALID_PID);
		return (B_FALSE);
	}

	if (map_proc_load(proc) != 0) {
		proc_refcount_dec(proc);
		win_warn_msg(WARN_INVALID_MAP);
		win_note_show(NOTE_INVALID_MAP);
		return (B_FALSE);
	}

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

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

void *
os_llcallchain_dyn_create(page_t *page)
{
	dyn_llcallchain_t *dyn;
	cmd_llcallchain_t *cmd = CMD_LLCALLCHAIN(&page->cmd);
	int i;

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

	dyn->pid = cmd->pid;
	dyn->lwpid = cmd->lwpid;
	dyn->addr = cmd->addr;
	dyn->size = cmd->size;
	strncpy(dyn->desc, cmd->desc, WIN_DESCBUF_SIZE);
	dyn->desc[WIN_DESCBUF_SIZE - 1] = 0;

	if ((i = reg_init(&dyn->msg, 0, 1, g_scr_width, 2, A_BOLD)) < 0)
		goto L_EXIT;
	if ((i = reg_init(&dyn->buf_caption, 0, i, g_scr_width, 2, A_BOLD | A_UNDERLINE)) < 0)
		goto L_EXIT;
	if ((i = reg_init(&dyn->buf_data, 0, i, g_scr_width, 1, 0)) < 0)
		goto L_EXIT;
	if ((i = reg_init(&dyn->chain_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 ((reg_init(&dyn->chain_data, 0, i, g_scr_width, g_scr_height - i - 2, 0)) < 0)
		goto L_EXIT;
	reg_buf_init(&dyn->chain_data, NULL, win_callchain_line_get);
	reg_scroll_init(&dyn->chain_data, B_TRUE);

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

void
os_llcallchain_win_destroy(dyn_win_t *win)
{
	dyn_llcallchain_t *dyn;

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

		reg_win_destroy(&dyn->msg);
		reg_win_destroy(&dyn->buf_caption);
		reg_win_destroy(&dyn->buf_data);
		reg_win_destroy(&dyn->chain_caption);
		reg_win_destroy(&dyn->pad);		
		reg_win_destroy(&dyn->chain_data);
		free(dyn);
	}
}

/*
 * The callback function used in bsearch() to compare the linear address.
 */
static int
bufaddr_cmp(const void *p1, const void *p2)
{
	const uint64_t addr = (const uint64_t)p1;
	const bufaddr_t *bufaddr = (const bufaddr_t *)p2;

	if (addr < bufaddr->addr) {
		return (-1);
	}

	if (addr >= bufaddr->addr + bufaddr->size) {
		return (1);
	}

	return (0);
}

static int
llcallchain_bufinfo_show(dyn_llcallchain_t *dyn, track_proc_t *proc,
	track_lwp_t *lwp)
{
	char content[WIN_LINECHAR_MAX];
	lat_line_t *lat_buf, *line;
	win_reg_t *reg;
	int lwpid = 0, nlines, lat;
	
	/*
	 * Display the caption of data table:
	 * "ADDR SIZE ACCESS% LAT(ns) DESC"
	 */
	reg = &dyn->buf_caption;
	reg_erase(reg);	
	(void) snprintf(content, sizeof (content),
	    "%16s%8s%11s%11s%34s",
	    CAPTION_ADDR, CAPTION_SIZE, CAPTION_BUFHIT,
	    CAPTION_AVGLAT, CAPTION_DESC);
	reg_line_write(&dyn->buf_caption, 1, ALIGN_LEFT, content);
	dump_write("%s\n", content);
	reg_refresh_nout(reg);
	
	/*
	 * Get the stat of buffer.
	 */	
	reg = &dyn->buf_data;
	reg_erase(reg);	

	if (lwp != NULL) {
		lwpid = lwp->id;	
	}
	 
	if ((lat_buf = win_lat_buf_create(proc, lwpid, &nlines)) == NULL) {
		return (-1);
	}

	/*
	 * Fill in the memory access information.
	 */
	win_lat_buf_fill(lat_buf, nlines, proc, lwp, &lat);
	
	/*
	 * Check if the linear address is located in a buffer in
	 * process address space.
	 */ 
	if ((line = bsearch((void *)(dyn->addr), lat_buf, nlines,
		sizeof (lat_line_t), bufaddr_cmp)) != NULL) {
		win_lat_str_build(content, WIN_LINECHAR_MAX, 0, line);
		reg_line_write(reg, 0, ALIGN_LEFT, content);
		dump_write("%s\n", content);
	}
	
	reg_refresh_nout(reg);
	free(lat_buf);
	return (0);
}

static int
llcallchain_list_show(dyn_llcallchain_t *dyn, track_proc_t *proc,
	track_lwp_t *lwp)
{
	perf_llrecgrp_t *llrec_grp;
	char content[WIN_LINECHAR_MAX];
	os_perf_llrec_t *rec_arr;
	sym_chainlist_t chainlist;
	win_reg_t *reg;
	int i;

	reg = &dyn->chain_caption;
	reg_erase(reg);	
	snprintf(content, WIN_LINECHAR_MAX, "Call-chain list:");
	reg_line_write(reg, 1, ALIGN_LEFT, content);
	dump_write("%s\n", content);
	reg_refresh_nout(reg);

	reg = &dyn->pad;
	reg_erase(reg);	
	dump_write("\n");
	reg_refresh_nout(reg);

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

	if (sym_load(proc, SYM_TYPE_FUNC) != 0) {
		debug_print(NULL, 2, "Failed to load the process symbol "
			"(pid = %d)\n", proc->pid);
		return (-1);
	}

	memset(&chainlist, 0, sizeof (sym_chainlist_t));
	rec_arr = llrec_grp->rec_arr;

	for (i = 0; i < llrec_grp->nrec_cur; i++) {
		if ((rec_arr[i].addr < dyn->addr) ||
			(rec_arr[i].addr >= dyn->addr + dyn->size)) {
			continue;
		}

		sym_callchain_add(&proc->sym, rec_arr[i].callchain.ips,
			rec_arr[i].callchain.ip_num, &chainlist);		
	}

	chainlist_show(&chainlist, &dyn->chain_data);
	return (0);
}

static void
llcallchain_data_show(dyn_win_t *win, boolean_t *note_out)
{
	dyn_llcallchain_t *dyn;
	pid_t pid;
	int lwpid;
	uint64_t size;
	track_proc_t *proc;
	track_lwp_t *lwp = NULL;
	win_reg_t *reg;
	char content[WIN_LINECHAR_MAX], intval_buf[16];
	char size_str[32];

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

	if ((proc = proc_find(pid)) == NULL) {
		win_warn_msg(WARN_INVALID_PID);
		win_note_show(NOTE_INVALID_PID);
		*note_out = B_TRUE;
		return;
	}

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

	reg = &dyn->msg;
	reg_erase(reg);	
	disp_intval(intval_buf, 16);
	win_size2str(size, size_str, sizeof (size_str));

	if (lwp == NULL) {
		(void) snprintf(content, WIN_LINECHAR_MAX,
			"Call-chain when process accesses the memory area (pid: %d)"
	    	" (interval: %s)", pid, intval_buf);
	} else {
		(void) snprintf(content, WIN_LINECHAR_MAX,
			"Call-chain when thread accesses the memory area (lwpid: %d)"
	    	" (interval: %s)", lwpid, intval_buf);
	}

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

	llcallchain_bufinfo_show(dyn, proc, lwp);
	llcallchain_list_show(dyn, proc, lwp);
	
	if (lwp != NULL) {
		lwp_refcount_dec(lwp);
	}

	proc_refcount_dec(proc);
}

/*
 * Display window on screen.
 * (window type: "WIN_TYPE_LLCALLCHAIN")
 */
boolean_t
os_llcallchain_win_draw(dyn_win_t *win)
{
	boolean_t note_out;

	win_title_show();
	llcallchain_data_show(win, &note_out);
	if (!note_out) {
		win_note_show(NOTE_LLCALLCHAIN);
	}

	reg_update_all();
	return (B_TRUE);
}

void
os_llcallchain_win_scroll(dyn_win_t *win, int scroll_type)
{
	dyn_llcallchain_t *dyn = (dyn_llcallchain_t *)(win->dyn);

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

/*
 * "lat_buf" points to an array which contains the process address mapping.
 * Each item in array represents a buffer in process address space. "rec"
 * points to a SMPL record. The function is responsible for checking and
 * comparing the record with the process address mapping.
 */
void
os_lat_buf_hit(lat_line_t *lat_buf, int nlines, os_perf_llrec_t *rec,
	uint64_t *total_lat, uint64_t *total_sample)
{
	lat_line_t *line;

	/*
	 * Check if the linear address is located in a buffer in
	 * process address space.
	 */
	if ((line = bsearch((void *)(rec->addr), lat_buf, nlines,
	    sizeof (lat_line_t), bufaddr_cmp)) != NULL) {
		/*
		 * If the linear address is located in, that means this
		 * buffer is accessed, so update the statistics of accessing.
		 */
		line->naccess++;
		line->latency += rec->latency;
		*total_lat += rec->latency;
		*total_sample += 1;
	}
}


static lat_line_t *
latnode_buf_create(track_proc_t *proc, int lwpid, uint64_t addr,
	uint64_t size, int *nlines)
{
	map_entry_t *entry;
	numa_entry_t *numa_entry;
	numa_map_t *numa_map;
	lat_line_t *buf;
	int i;

	if ((entry = map_entry_find(proc, addr, size)) == NULL) {
		return (NULL);
	}
	
	numa_map = &entry->numa_map;
	*nlines = numa_map->nentry_cur;
	if ((buf = zalloc(sizeof (lat_line_t) * (*nlines))) == NULL) {
		return (NULL);
	}

	for (i = 0; i < *nlines; i++) {
		numa_entry = &numa_map->arr[i];
		buf[i].pid = proc->pid;
		buf[i].lwpid = lwpid;
		buf[i].nid_show = B_TRUE;
		buf[i].bufaddr.addr = numa_entry->start_addr;
		buf[i].bufaddr.size = numa_entry->end_addr - numa_entry->start_addr;
		buf[i].nid = numa_entry->nid;		
	}

	return (buf);
}

static int
latnode_data_get(track_proc_t *proc, track_lwp_t *lwp, dyn_latnode_t *dyn)
{
	lat_line_t *buf;
	char content[WIN_LINECHAR_MAX];
	int nlines, lwpid = 0, lat = 0;

	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 ((buf = latnode_buf_create(proc, lwpid, dyn->addr,
		dyn->size, &nlines)) == NULL) {
		reg_line_write(&dyn->caption, 1, ALIGN_LEFT,
		    "Failed to get the process NUMA mapping!");
		reg_refresh_nout(&dyn->caption);
		return (-1);
	}

	win_lat_buf_fill(buf, nlines, proc, lwp, &lat);

	/*
	 * Sort by the number of buffer accessing.
	 */
	qsort(buf, nlines, sizeof (lat_line_t), win_lat_cmp);

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

	reg_line_write(&dyn->caption, 1, ALIGN_LEFT, 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 *)buf;
	reg_scroll_show(&dyn->data, (void *)(dyn->data.buf),
	    nlines, win_lat_str_build);
	reg_refresh_nout(&dyn->data);

	return (0);
}

static boolean_t
latnode_data_show(track_proc_t *proc, dyn_latnode_t *dyn,
	map_entry_t *entry __attribute__((unused)),
	boolean_t *note_out)
{
	win_reg_t *reg;
	track_lwp_t *lwp = NULL;
	char content[WIN_LINECHAR_MAX], intval_buf[16], size_str[32];

	*note_out = B_FALSE;

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

	reg = &dyn->msg;
	reg_erase(reg);
	disp_intval(intval_buf, 16);
	(void) snprintf(content, sizeof (content),
	    "Break down of memory area for physical memory on node (interval: %s)",
	    intval_buf);
	reg_line_write(reg, 1, ALIGN_LEFT, content);
	dump_write("\n*** %s\n", content);
	reg_refresh_nout(reg);

	reg = &dyn->note;
	reg_erase(reg);	
	win_size2str(dyn->size, size_str, sizeof (size_str));
	if (lwp != NULL) {
		(void) snprintf(content, sizeof (content),
		    "Memory area(%"PRIX64", %s), thread(%d)",
		    dyn->addr, size_str, lwp->id);
	} else {
		(void) snprintf(content, sizeof (content),
		    "Memory area(%"PRIX64", %s), process(%d)",
		    dyn->addr, size_str, proc->pid);
	}

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

	latnode_data_get(proc, lwp, dyn);

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

	return (B_TRUE);
}

/*
 * The implementation of displaying window on screen for
 * window type "WIN_TYPE_LATNODE_PROC" and "WIN_TYPE_LATNODE_LWP"
 */
boolean_t
os_latnode_win_draw(dyn_win_t *win)
{
	dyn_latnode_t *dyn = (dyn_latnode_t *)(win->dyn);
	track_proc_t *proc;
	map_entry_t *entry;
	boolean_t note_out, ret;

	if ((proc = proc_find(dyn->pid)) == NULL) {
		win_warn_msg(WARN_INVALID_PID);
		win_note_show(NOTE_INVALID_PID);
		return (B_FALSE);
	}

	if ((entry = map_entry_find(proc, dyn->addr, dyn->size)) == NULL) {
		proc_refcount_dec(proc);
		win_warn_msg(WARN_INVALID_MAP);
		win_note_show(NOTE_INVALID_MAP);
		return (B_FALSE);
	}

	if (map_map2numa(proc, entry) != 0) {
		proc_refcount_dec(proc);
		win_warn_msg(WARN_INVALID_NUMAMAP);
		win_note_show(NOTE_INVALID_NUMAMAP);
		return (B_FALSE);		
	}

	win_title_show();
	ret = latnode_data_show(proc, dyn, entry, &note_out);
	if (!note_out) {
		win_note_show(NOTE_LATNODE);
	}

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