numatop/0000775000175000017500000000000012633417410012110 5ustar jinyaojinyaonumatop/COPYING0000664000175000017500000000271512633407552013156 0ustar jinyaojinyaoCopyright (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. numatop/README0000664000175000017500000000251212633407552012776 0ustar jinyaojinyaoBuilding & Installing NumaTOP ------------------------------ 1. To build NumaTOP make 2. To clean the built objects make clean 3. To install NumaTOP (needs root authority). make install Build Dependencies ------------------ NumaTOP requires following libraries: 1. numactl-devel or libnuma-dev(el) 2. libncurses 3. libpthread Kernel Requirement: ------------------ Recommended kernel: 3.16 For Haswell supporting, please also apply a perf patch on 3.16. The patch is numatop/kernel_patches/0001-perf-x86-Widen-Haswell-OFFCORE-mask.patch. The patch can also be found at following link: http://www.gossamer-threads.com/lists/linux/kernel/1964864 Directories ------------------ common: common code for all platforms. intel : Intel platform-specific code. test : mgen source code. mgen is a micro-test application which can generate memory access with runtime latency value among CPUs. Note that this application is only used for numatop testing! kernel_patches: the required kernel patches. Note: ----- numatop is supported on Intel Xeon processors: 5500-series, 6500/7500-series, 5600 series, E7-x8xx-series, and E5-16xx/24xx/26xx/46xx-series. E5-16xx/24xx/26xx/46xx-series had better be updated to latest CPU microcode (microcode must be 0x618+ or 0x70c+). To learn about NumaTOP, please visit http://01.org/numatop numatop/common/0000775000175000017500000000000012633407552013406 5ustar jinyaojinyaonumatop/common/lwp.c0000664000175000017500000001753512633407552014367 0ustar jinyaojinyao/* * 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 handle the 'tracked thread'. */ #include #include #include #include #include #include #include #include #include "include/types.h" #include "include/util.h" #include "include/lwp.h" #include "include/proc.h" #include "include/perf.h" #include "include/os/node.h" #include "include/os/os_util.h" /* * Allocation and initialization for a 'track_lwp_t' struture. * A 'track_lwp_t' structure is allocated for each tracked thread. */ static track_lwp_t * lwp_alloc(void) { int cpuid_max; track_lwp_t *lwp; count_value_t *countval_arr; boolean_t supported; if ((cpuid_max = node_cpuid_max()) <= 0) { return (NULL); } if ((countval_arr = zalloc(cpuid_max * sizeof (count_value_t))) == NULL) { return (NULL); } if ((lwp = zalloc(sizeof (track_lwp_t))) == NULL) { free(countval_arr); return (NULL); } if (((lwp->perf_priv = perf_priv_alloc(&supported)) == NULL) && (supported)) { free(countval_arr); free(lwp); return (NULL); } if (pthread_mutex_init(&lwp->mutex, NULL) != 0) { free(countval_arr); perf_priv_free(lwp->perf_priv); free(lwp); return (NULL); } lwp->countval_arr = countval_arr; lwp->cpuid_max = cpuid_max; lwp->inited = B_TRUE; return (lwp); } /* * Clean up the resource of 'track_lwp_t' struture. Before calling * lwp_free(), 'track_lwp_t' must be removed from 'lwp_list' in * 'track_proc_t' structure. */ int lwp_free(track_lwp_t *lwp) { (void) pthread_mutex_lock(&lwp->mutex); if (lwp->ref_count > 0) { lwp->removing = B_TRUE; (void) pthread_mutex_unlock(&lwp->mutex); return (-1); } if (lwp->countval_arr != NULL) { free(lwp->countval_arr); } perf_priv_free(lwp->perf_priv); perf_countchain_reset(&lwp->count_chain); perf_llrecgrp_reset(&lwp->llrec_grp); (void) pthread_mutex_unlock(&lwp->mutex); (void) pthread_mutex_destroy(&lwp->mutex); free(lwp); return (0); } /* * Move the 'sort_idx' to next lwp node and return current one. */ track_lwp_t * lwp_sort_next(track_proc_t *proc) { proc_lwplist_t *list = &proc->lwp_list; int idx = list->sort_idx; if (list->sort_arr == NULL) { return (NULL); } if (idx < list->nlwps) { list->sort_idx++; return (list->sort_arr[idx]); } return (NULL); } static int id_cmp(const void *a, const void *b) { int *id1 = (int *)a; int *id2 = (int *)b; if (*id1 > *id2) { return (1); } if (*id1 < *id2) { return (-1); } return (0); } /* * Enumerate valid threads from '/proc', remove the obsolete threads. */ void lwp_enum_update(track_proc_t *proc) { proc_lwplist_t *list = &proc->lwp_list; track_lwp_t *lwp; track_lwp_t **arr_new, **arr_old; int *lwps_new, nlwp_new; int i = 0, j = 0, k; if (os_procfs_lwp_enum(proc->pid, &lwps_new, &nlwp_new) != 0) { return; } qsort(lwps_new, nlwp_new, sizeof (int), id_cmp); if ((arr_new = zalloc(sizeof (track_lwp_t *) * nlwp_new)) == NULL) { goto L_EXIT; } (void) pthread_mutex_lock(&proc->mutex); if ((arr_old = list->id_arr) != NULL) { while ((i < nlwp_new) && (j < list->nlwps)) { if (lwps_new[i] == arr_old[j]->id) { arr_new[i] = arr_old[j]; i++; j++; continue; } if (lwps_new[i] < arr_old[j]->id) { if ((lwp = lwp_alloc()) != NULL) { lwp->id = lwps_new[i]; lwp->proc = proc; arr_new[i] = lwp; } i++; continue; } /* The lwpid (arr_old[j]->id) is obsolete */ (void) lwp_free(arr_old[j]); j++; } } for (k = i; k < nlwp_new; k++) { if ((lwp = lwp_alloc()) != NULL) { lwp->id = lwps_new[k]; lwp->proc = proc; arr_new[k] = lwp; } } if (arr_old != NULL) { for (k = j; k < list->nlwps; k++) { (void) lwp_free(arr_old[k]); } free(arr_old); } list->id_arr = arr_new; list->nlwps = nlwp_new; (void) pthread_mutex_unlock(&proc->mutex); L_EXIT: free(lwps_new); } /* * lwp refcount increment. The 'track_lwp_t' structure can * be only released when the refcount is 0. */ int lwp_refcount_inc(track_lwp_t *lwp) { int ret = -1; (void) pthread_mutex_lock(&lwp->mutex); if ((!lwp->removing) && (!lwp->quitting)) { lwp->ref_count++; ret = 0; } (void) pthread_mutex_unlock(&lwp->mutex); return (ret); } /* * lwp refcount decrement. If the refcount is 0 after decrement * and the 'removing' flag is set, release the 'track_lwp_t' structure. */ void lwp_refcount_dec(track_lwp_t *lwp) { boolean_t remove = B_FALSE; (void) pthread_mutex_lock(&lwp->mutex); lwp->ref_count--; if ((lwp->ref_count == 0) && (lwp->removing)) { remove = B_TRUE; } (void) pthread_mutex_unlock(&lwp->mutex); if (remove) { (void) lwp_free(lwp); } } static uint64_t count_value_get(track_lwp_t *lwp, count_id_t count_id) { return (node_countval_sum(lwp->countval_arr, lwp->cpuid_max, NODE_ALL, count_id)); } /* * Compute the value of key for lwp sorting. */ int lwp_key_compute(track_lwp_t *lwp, void *arg, boolean_t *end) { sort_key_t sortkey = *((sort_key_t *)arg); switch (sortkey) { case SORT_KEY_CPU: lwp->key = count_value_get(lwp, COUNT_CLK); break; default: break; } *end = B_FALSE; return (0); } /* * Update the lwp's per CPU perf data. */ int lwp_countval_update(track_lwp_t *lwp, int cpu, count_id_t count_id, uint64_t value) { count_value_t *countval, *arr_new; int cpuid_max = node_cpuid_max(); /* * Check if new cpu hotadd/online */ if (cpu >= lwp->cpuid_max) { ASSERT(cpuid_max > lwp->cpuid_max); if ((arr_new = realloc(lwp->countval_arr, sizeof (count_value_t) * cpuid_max)) == NULL) { return (-1); } (void) memset(&arr_new[lwp->cpuid_max], 0, sizeof (count_value_t) * (cpuid_max - lwp->cpuid_max)); lwp->countval_arr = arr_new; lwp->cpuid_max = cpuid_max; } countval = &lwp->countval_arr[cpu]; countval->counts[count_id] += value; return (0); } int lwp_intval_get(track_lwp_t *lwp) { return (lwp->intval_ms); } static int intval_update(track_lwp_t *lwp, void *arg, boolean_t *end) { int intval_ms = *((int *)arg); *end = B_FALSE; lwp->intval_ms = intval_ms; return (0); } /* * Update with the sampling interval for all threads in process. */ void lwp_intval_update(track_proc_t *proc, int intval_ms) { (void) pthread_mutex_lock(&proc->mutex); proc_lwp_traverse(proc, intval_update, &intval_ms); (void) pthread_mutex_unlock(&proc->mutex); } void lwp_quitting_set(track_lwp_t *lwp) { lwp->quitting = B_TRUE; } numatop/common/os/0000775000175000017500000000000012633415311014017 5ustar jinyaojinyaonumatop/common/os/os_win.c0000664000175000017500000005570712633407552015507 0ustar jinyaojinyao/* * 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 #include #include #include #include #include #include #include #include #include #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/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) { int *id1 = (int *)a; int *id2 = (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), "%-20s%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]; node_t *node; win_countvalue_t value; node_meminfo_t meminfo; int i = 1; 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++); 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; 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)); rec_grp = &count_chain->chaingrps[dyn->countid]; rec_arr = rec_grp->rec_arr; for (i = 0; i < rec_grp->nrec_cur; i++) { sym_callchain_add(&proc->sym, rec_arr[i].callchain.ips, rec_arr[i].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, ¬e_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) { uint64_t addr = (uint64_t)p1; bufaddr_t *bufaddr = (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, ¬e_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, 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, ¬e_out); if (!note_out) { win_note_show(NOTE_LATNODE); } proc_refcount_dec(proc); reg_update_all(); return (ret); } numatop/common/os/pfwrapper.c0000664000175000017500000003403212633407552016203 0ustar jinyaojinyao/* * 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 process the syscall "perf_event_open" */ #include #include #include #include #include #include #include #include #include "../include/types.h" #include "../include/perf.h" #include "../include/util.h" #include "../include/os/pfwrapper.h" static int s_mapsize, s_mapmask; static int pf_event_open(struct perf_event_attr *attr, pid_t pid, int cpu, int group_fd, unsigned long flags) { return (syscall(__NR_perf_event_open, attr, pid, cpu, group_fd, flags)); } static int mmap_buffer_read(struct perf_event_mmap_page *header, void *buf, size_t size) { void *data; uint64_t data_head, data_tail; int data_size, ncopies; /* * The first page is a meta-data page (struct perf_event_mmap_page), * so move to the second page which contains the perf data. */ data = (void *)header + g_pagesize; /* * data_tail points to the position where userspace last read, * data_head points to the position where kernel last add. * After read data_head value, need to issue a rmb(). */ data_tail = header->data_tail; data_head = header->data_head; rmb(); /* * The kernel function "perf_output_space()" guarantees no data_head can * wrap over the data_tail. */ if ((data_size = data_head - data_tail) < size) { return (-1); } data_tail &= s_mapmask; /* * Need to consider if data_head is wrapped when copy data. */ if ((ncopies = (s_mapsize - data_tail)) < size) { memcpy(buf, data + data_tail, ncopies); memcpy(buf + ncopies, data, size - ncopies); } else { memcpy(buf, data + data_tail, size); } header->data_tail += size; return (0); } static void mmap_buffer_skip(struct perf_event_mmap_page *header, int size) { int data_head; data_head = header->data_head; rmb(); if ((header->data_tail + size) > data_head) { size = data_head - header->data_tail; } header->data_tail += size; } static void mmap_buffer_reset(struct perf_event_mmap_page *header) { int data_head; data_head = header->data_head;; rmb(); header->data_tail = data_head; } int pf_ringsize_init(void) { switch (g_precise) { case PRECISE_HIGH: s_mapsize = g_pagesize * (PF_MAP_NPAGES_MAX + 1); s_mapmask = (g_pagesize * PF_MAP_NPAGES_MAX) - 1; break; case PRECISE_LOW: s_mapsize = g_pagesize * (PF_MAP_NPAGES_MIN + 1); s_mapmask = (g_pagesize * PF_MAP_NPAGES_MIN) - 1; break; default: s_mapsize = g_pagesize * (PF_MAP_NPAGES_NORMAL + 1); s_mapmask = (g_pagesize * PF_MAP_NPAGES_NORMAL) - 1; break; } return (s_mapsize - g_pagesize); } int pf_profiling_setup(struct _perf_cpu *cpu, int idx, pf_conf_t *conf) { struct perf_event_attr attr; int *fds = cpu->fds; int group_fd; memset(&attr, 0, sizeof (attr)); attr.type = conf->type; attr.config = conf->config; attr.config1 = conf->config1; attr.sample_period = conf->sample_period; attr.sample_type = PERF_SAMPLE_TID | PERF_SAMPLE_READ | PERF_SAMPLE_CALLCHAIN; attr.read_format = PERF_FORMAT_GROUP | PERF_FORMAT_TOTAL_TIME_ENABLED | PERF_FORMAT_TOTAL_TIME_RUNNING; if (idx == 0) { attr.disabled = 1; group_fd = -1; } else { group_fd = fds[0];; } if ((fds[idx] = pf_event_open(&attr, -1, cpu->cpuid, group_fd, 0)) < 0) { debug_print(NULL, 2, "pf_profiling_setup: pf_event_open is failed " "for CPU%d, COUNT%d\n", cpu->cpuid, idx); fds[idx] = INVALID_FD; return (-1); } if (idx == 0) { if ((cpu->map_base = mmap(NULL, s_mapsize, PROT_READ | PROT_WRITE, MAP_SHARED, fds[0], 0)) == MAP_FAILED) { close(fds[0]); fds[0] = INVALID_FD; return (-1); } cpu->map_len = s_mapsize; cpu->map_mask = s_mapmask; } else { if (ioctl(fds[idx], PERF_EVENT_IOC_SET_OUTPUT, fds[0]) != 0) { debug_print(NULL, 2, "pf_profiling_setup: " "PERF_EVENT_IOC_SET_OUTPUT is failed for CPU%d, COUNT%d\n", cpu->cpuid, idx); close(fds[idx]); fds[idx] = INVALID_FD; return (-1); } } return (0); } int pf_profiling_start(struct _perf_cpu *cpu, count_id_t count_id) { if (cpu->fds[count_id] != INVALID_FD) { return (ioctl(cpu->fds[count_id], PERF_EVENT_IOC_ENABLE, 0)); } return (0); } int pf_profiling_stop(struct _perf_cpu *cpu, count_id_t count_id) { if (cpu->fds[count_id] != INVALID_FD) { return (ioctl(cpu->fds[count_id], PERF_EVENT_IOC_DISABLE, 0)); } return (0); } int pf_profiling_allstart(struct _perf_cpu *cpu) { return (pf_profiling_start(cpu, 0)); } int pf_profiling_allstop(struct _perf_cpu *cpu) { return (pf_profiling_stop(cpu, 0)); } static uint64_t scale(uint64_t value, uint64_t time_enabled, uint64_t time_running) { uint64_t res = 0; if (time_running > time_enabled) { debug_print(NULL, 2, "time_running > time_enabled\n"); } if (time_running) { res = (uint64_t)((double)value * (double)time_enabled / (double)time_running); } return (res); } static int profiling_sample_read(struct perf_event_mmap_page *mhdr, int size, pf_profiling_rec_t *rec) { struct { uint32_t pid, tid; } id; count_value_t *countval = &rec->countval; uint64_t i, time_enabled, time_running, nr, value, *ips; int j, ret = -1; /* * struct read_format { * { u32 pid, tid; } * { u64 nr; } * { u64 time_enabled; } * { u64 time_running; } * { u64 cntr[nr]; } * [ u64 nr; } * { u64 ips[nr]; } * }; */ if (mmap_buffer_read(mhdr, &id, sizeof (id)) == -1) { debug_print(NULL, 2, "profiling_sample_read: read pid/tid failed.\n"); goto L_EXIT; } size -= sizeof (id); if (mmap_buffer_read(mhdr, &nr, sizeof (nr)) == -1) { debug_print(NULL, 2, "profiling_sample_read: read nr failed.\n"); goto L_EXIT; } size -= sizeof (nr); if (mmap_buffer_read(mhdr, &time_enabled, sizeof (time_enabled)) == -1) { debug_print(NULL, 2, "profiling_sample_read: read time_enabled failed.\n"); goto L_EXIT; } size -= sizeof (time_enabled); if (mmap_buffer_read(mhdr, &time_running, sizeof (time_running)) == -1) { debug_print(NULL, 2, "profiling_sample_read: read time_running failed.\n"); goto L_EXIT; } size -= sizeof (time_running); for (i = 0; i < nr; i++) { if (mmap_buffer_read(mhdr, &value, sizeof (value)) == -1) { debug_print(NULL, 2, "profiling_sample_read: read value failed.\n"); goto L_EXIT; } size -= sizeof (value); /* * Prevent the inconsistent results if share the PMU with other users * who multiplex globally. */ value = scale(value, time_enabled, time_running); countval->counts[i] = value; } if (mmap_buffer_read(mhdr, &nr, sizeof (nr)) == -1) { debug_print(NULL, 2, "profiling_sample_read: read nr failed.\n"); goto L_EXIT; } size -= sizeof (nr); j = 0; ips = rec->ips; for (i = 0; i < nr; i++) { if (j >= IP_NUM) { break; } if (mmap_buffer_read(mhdr, &value, sizeof (value)) == -1) { debug_print(NULL, 2, "profiling_sample_read: read value failed.\n"); return (-1); } size -= sizeof (value); if (value < KERNEL_ADDR_START) { /* * Only save the user-space address. */ ips[j] = value; j++; } } rec->ip_num = j; rec->pid = id.pid; rec->tid = id.tid; ret = 0; L_EXIT: if (size > 0) { mmap_buffer_skip(mhdr, size); debug_print(NULL, 2, "profiling_sample_read: skip %d bytes, ret=%d\n", size, ret); } return (ret); } static void profiling_recbuf_update(pf_profiling_rec_t *rec_arr, int *nrec, pf_profiling_rec_t *rec) { int i; if ((rec->pid == 0) || (rec->tid == 0)) { /* Just consider the user-land process/thread. */ return; } /* * The buffer of array is enough, don't need to consider overflow. */ i = *nrec; memcpy(&rec_arr[i], rec, sizeof (pf_profiling_rec_t)); *nrec += 1; } void pf_profiling_record(struct _perf_cpu *cpu, pf_profiling_rec_t *rec_arr, int *nrec) { struct perf_event_mmap_page *mhdr = cpu->map_base; struct perf_event_header ehdr; pf_profiling_rec_t rec; int size; if (nrec != NULL) { *nrec = 0; } for (;;) { if (mmap_buffer_read(mhdr, &ehdr, sizeof(ehdr)) == -1) { return; } if ((size = ehdr.size - sizeof (ehdr)) <= 0) { mmap_buffer_reset(mhdr); return; } if ((ehdr.type == PERF_RECORD_SAMPLE) && (rec_arr != NULL)) { if (profiling_sample_read(mhdr, size, &rec) == 0) { profiling_recbuf_update(rec_arr, nrec, &rec); } else { /* No valid record in ring buffer. */ return; } } else { mmap_buffer_skip(mhdr, size); } } } int pf_ll_setup(struct _perf_cpu *cpu, pf_conf_t *conf) { struct perf_event_attr attr; int *fds = cpu->fds; memset(&attr, 0, sizeof (attr)); attr.type = conf->type; attr.config = conf->config; attr.config1 = conf->config1; attr.sample_period = conf->sample_period; attr.precise_ip = 1; attr.exclude_guest = 1; attr.sample_type = PERF_SAMPLE_TID | PERF_SAMPLE_ADDR | PERF_SAMPLE_CPU | PERF_SAMPLE_WEIGHT | PERF_SAMPLE_CALLCHAIN; attr.disabled = 1; if ((fds[0] = pf_event_open(&attr, -1, cpu->cpuid, -1, 0)) < 0) { debug_print(NULL, 2, "pf_ll_setup: pf_event_open is failed " "for CPU%d\n", cpu->cpuid); fds[0] = INVALID_FD; return (-1); } if ((cpu->map_base = mmap(NULL, s_mapsize, PROT_READ | PROT_WRITE, MAP_SHARED, fds[0], 0)) == MAP_FAILED) { close(fds[0]); fds[0] = INVALID_FD; return (-1); } cpu->map_len = s_mapsize; cpu->map_mask = s_mapmask; return (0); } int pf_ll_start(struct _perf_cpu *cpu) { if (cpu->fds[0] != INVALID_FD) { return (ioctl(cpu->fds[0], PERF_EVENT_IOC_ENABLE, 0)); } return (0); } int pf_ll_stop(struct _perf_cpu *cpu) { if (cpu->fds[0] != INVALID_FD) { return (ioctl(cpu->fds[0], PERF_EVENT_IOC_DISABLE, 0)); } return (0); } static int ll_sample_read(struct perf_event_mmap_page *mhdr, int size, pf_ll_rec_t *rec) { struct { uint32_t pid, tid; } id; uint64_t i, addr, cpu, weight, nr, value, *ips; int j, ret = -1; /* * struct read_format { * { u32 pid, tid; } * { u64 addr; } * { u64 cpu; } * [ u64 nr; } * { u64 ips[nr]; } * { u64 weight; } * }; */ if (mmap_buffer_read(mhdr, &id, sizeof (id)) == -1) { debug_print(NULL, 2, "ll_sample_read: read pid/tid failed.\n"); goto L_EXIT; } size -= sizeof (id); if (mmap_buffer_read(mhdr, &addr, sizeof (addr)) == -1) { debug_print(NULL, 2, "ll_sample_read: read addr failed.\n"); goto L_EXIT; } size -= sizeof (addr); if (mmap_buffer_read(mhdr, &cpu, sizeof (cpu)) == -1) { debug_print(NULL, 2, "ll_sample_read: read cpu failed.\n"); goto L_EXIT; } size -= sizeof (cpu); if (mmap_buffer_read(mhdr, &nr, sizeof (nr)) == -1) { debug_print(NULL, 2, "ll_sample_read: read nr failed.\n"); goto L_EXIT; } size -= sizeof (nr); j = 0; ips = rec->ips; for (i = 0; i < nr; i++) { if (j >= IP_NUM) { break; } if (mmap_buffer_read(mhdr, &value, sizeof (value)) == -1) { debug_print(NULL, 2, "ll_sample_read: read ip failed.\n"); goto L_EXIT; } size -= sizeof (value); if (value < KERNEL_ADDR_START) { /* * Only save the user-space address. */ ips[j] = value; j++; } } if (mmap_buffer_read(mhdr, &weight, sizeof (weight)) == -1) { debug_print(NULL, 2, "ll_sample_read: read weight failed.\n"); goto L_EXIT; } size -= sizeof (weight); rec->ip_num = j; rec->pid = id.pid; rec->tid = id.tid; rec->addr = addr; rec->cpu = cpu; rec->latency = weight; ret = 0; L_EXIT: if (size > 0) { mmap_buffer_skip(mhdr, size); debug_print(NULL, 2, "ll_sample_read: skip %d bytes, ret=%d\n", size, ret); } return (ret); } static void ll_recbuf_update(pf_ll_rec_t *rec_arr, int *nrec, pf_ll_rec_t *rec) { int i; if ((rec->pid == 0) || (rec->tid == 0)) { /* Just consider the user-land process/thread. */ return; } /* * The size of array is enough. */ i = *nrec; memcpy(&rec_arr[i], rec, sizeof (pf_ll_rec_t)); *nrec += 1; } void pf_ll_record(struct _perf_cpu *cpu, pf_ll_rec_t *rec_arr, int *nrec) { struct perf_event_mmap_page *mhdr = cpu->map_base; struct perf_event_header ehdr; pf_ll_rec_t rec; int size; *nrec = 0; for (;;) { if (mmap_buffer_read(mhdr, &ehdr, sizeof(ehdr)) == -1) { /* No valid record in ring buffer. */ return; } if ((size = ehdr.size - sizeof (ehdr)) <= 0) { return; } if ((ehdr.type == PERF_RECORD_SAMPLE) && (rec_arr != NULL)) { if (ll_sample_read(mhdr, size, &rec) == 0) { ll_recbuf_update(rec_arr, nrec, &rec); } else { /* No valid record in ring buffer. */ return; } } else { mmap_buffer_skip(mhdr, size); } } } void pf_resource_free(struct _perf_cpu *cpu) { int i; for (i = 0; i < COUNT_NUM; i++) { if (cpu->fds[i] != INVALID_FD) { close(cpu->fds[i]); cpu->fds[i] = INVALID_FD; } } if (cpu->map_base != MAP_FAILED) { munmap(cpu->map_base, cpu->map_len); cpu->map_base = MAP_FAILED; cpu->map_len = 0; } } numatop/common/os/os_cmd.c0000664000175000017500000001412712633407552015444 0ustar jinyaojinyao/* * 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 #include #include #include #include #include #include #include "../include/types.h" #include "../include/page.h" #include "../include/win.h" #include "../include/perf.h" #include "../include/cmd.h" #include "../include/os/map.h" #include "../include/os/os_cmd.h" static int s_callchain_countid[] = { COUNT_RMA, COUNT_LMA, COUNT_CLK, COUNT_IR }; /* ARGSUSED */ int os_preop_switch2profiling(cmd_t *cmd, boolean_t *smpl) { *smpl = B_FALSE; if (!perf_profiling_started()) { perf_allstop(); if (perf_profiling_start() != 0) { return (-1); } *smpl = B_TRUE; } return (0); } int os_preop_switch2ll(cmd_t *cmd, boolean_t *smpl) { *smpl = B_FALSE; if (!perf_ll_started()) { perf_allstop(); if (perf_ll_start(0) != 0) { return (-1); } *smpl = B_TRUE; } return (0); } /* ARGSUSED */ int os_preop_llrefresh(cmd_t *cmd, boolean_t *smpl) { /* Not supported on Linux. */ return (0); } /* ARGSUSED */ int os_preop_llmap_get(cmd_t *cmd, boolean_t *smpl) { /* Not supported on Linux. */ return (0); } int os_preop_switch2ln(cmd_t *cmd, boolean_t *smpl) { /* Not supported on Linux. */ return (0); } /* ARGSUSED */ int os_preop_lnrefresh(cmd_t *cmd, boolean_t *smpl) { /* Not supported on Linux. */ return (0); } /* ARGSUSED */ int os_preop_lnmap_get(cmd_t *cmd, boolean_t *smpl) { /* Not supported on Linux. */ return (0); } /* ARGSUSED */ int os_preop_back2ll(cmd_t *cmd, boolean_t *smpl) { /* Not supported on Linux. */ return (0); } int os_preop_switch2callchain(cmd_t *cmd, boolean_t *smpl) { page_t *cur = page_current_get(); win_type_t type = PAGE_WIN_TYPE(cur); switch (type) { case WIN_TYPE_MONIPROC: CMD_CALLCHAIN(cmd)->pid = DYN_MONI_PROC(cur)->pid; CMD_CALLCHAIN(cmd)->lwpid = 0; break; case WIN_TYPE_MONILWP: CMD_CALLCHAIN(cmd)->pid = DYN_MONI_LWP(cur)->pid; CMD_CALLCHAIN(cmd)->lwpid = DYN_MONI_LWP(cur)->lwpid; break; default: return (-1); } *smpl = B_TRUE; return (perf_profiling_partpause(COUNT_RMA)); } int os_preop_switch2accdst(cmd_t *cmd, boolean_t *smpl) { page_t *cur = page_current_get(); win_type_t type = PAGE_WIN_TYPE(cur); switch (type) { case WIN_TYPE_LAT_PROC: CMD_ACCDST(cmd)->pid = DYN_LAT(cur)->pid; CMD_ACCDST(cmd)->lwpid = 0; break; case WIN_TYPE_LAT_LWP: CMD_ACCDST(cmd)->pid = DYN_LAT(cur)->pid; CMD_ACCDST(cmd)->lwpid = DYN_LAT(cur)->lwpid; break; default: return (-1); } return (0); } int os_preop_leavecallchain(cmd_t *cmd, boolean_t *smpl) { page_t *cur = page_current_get(); count_id_t countid; if ((countid = DYN_CALLCHAIN(cur)->countid) != 0) { perf_profiling_restore(countid); } *smpl = B_TRUE; return (0); } int os_op_llmap_stop(cmd_t *cmd, boolean_t smpl) { /* Not supported on Linux. */ return (0); } int os_op_lnmap_stop(cmd_t *cmd, boolean_t smpl) { /* Not supported on Linux. */ return (0); } int os_op_switch2ll(cmd_t *cmd, boolean_t smpl) { page_t *cur = page_current_get(); int type = PAGE_WIN_TYPE(cur); switch (type) { case WIN_TYPE_MONIPROC: CMD_LAT(cmd)->pid = DYN_MONI_PROC(cur)->pid; CMD_LAT(cmd)->lwpid = 0; break; case WIN_TYPE_MONILWP: CMD_LAT(cmd)->pid = DYN_MONI_LWP(cur)->pid; CMD_LAT(cmd)->lwpid = DYN_MONI_LWP(cur)->lwpid; break; default: return (-1); } return (op_page_next(cmd, smpl)); } static count_id_t callchain_countid_set(int cmd_id, page_t *page) { dyn_callchain_t *dyn = (dyn_callchain_t *)(page->dyn_win.dyn); if ((cmd_id >= CMD_1_ID) && (cmd_id <= CMD_4_ID)) { dyn->countid = s_callchain_countid[cmd_id - CMD_1_ID]; } else { dyn->countid = COUNT_INVALID; } return (dyn->countid); } int os_op_callchain_count(cmd_t *cmd, boolean_t smpl) { page_t *cur; count_id_t countid; int cmd_id; if ((cur = page_current_get()) != NULL) { cmd_id = CMD_ID(cmd); if ((countid = callchain_countid_set(cmd_id, cur)) == COUNT_INVALID) { return (0); } perf_profiling_partpause(countid); op_refresh(cmd, smpl); } return (0); } /* ARGSUSED */ int os_op_switch2llcallchain(cmd_t *cmd1, boolean_t smpl) { cmd_llcallchain_t *cmd = (cmd_llcallchain_t *)cmd1; page_t *cur = page_current_get(); dyn_lat_t *dyn; win_reg_t *data_reg; lat_line_t *lines; int i; dyn = (dyn_lat_t *)(cur->dyn_win.dyn); data_reg = &dyn->data; if ((lines = (lat_line_t *)(data_reg->buf)) == NULL) { return (-1); } if ((i = data_reg->scroll.highlight) == -1) { return (-1); } cmd->pid = dyn->pid; cmd->lwpid = dyn->lwpid; cmd->addr = lines[i].bufaddr.addr; cmd->size = lines[i].bufaddr.size; return (op_page_next(cmd1, smpl)); } numatop/common/os/node.c0000664000175000017500000002273212633407552015126 0ustar jinyaojinyao/* * 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 handle the node. */ #include #include #include #include #include #include #include #include #include "../include/types.h" #include "../include/util.h" #include "../include/os/os_util.h" #include "../include/os/pfwrapper.h" #include "../include/os/node.h" static node_group_t s_node_group; static void node_init(node_t *node, int nid, boolean_t hotadd) { memset(node, 0, sizeof (node_t)); os_perf_cpuarr_init(node->cpus, NCPUS_NODE_MAX, hotadd); node->nid = nid; node->hotadd = hotadd; } static void node_fini(node_t *node) { os_perf_cpuarr_fini(node->cpus, NCPUS_NODE_MAX, B_FALSE); node->ncpus = 0; node->nid = INVALID_NID; } static void node_hotremove(node_t *node) { node->hotremove = B_TRUE; os_perf_cpuarr_fini(node->cpus, NCPUS_NODE_MAX, B_TRUE); } /* * Initialization for the node group. */ int node_group_init(void) { int i; node_t *node; (void) memset(&s_node_group, 0, sizeof (node_group_t)); if (pthread_mutex_init(&s_node_group.mutex, NULL) != 0) { return (-1); } s_node_group.inited = B_TRUE; for (i = 0; i < NNODES_MAX; i++) { node = node_get(i); node_init(node, INVALID_NID, B_FALSE); } return (node_group_refresh(B_TRUE)); } /* * Free the resources of all nodes in group. */ static void node_group_reset(void) { node_t *node; int i; for (i = 0; i < NNODES_MAX; i++) { node = node_get(i); node_fini(node); } s_node_group.nnodes = 0; } /* * Clean up all the resources of node group. */ void node_group_fini(void) { if (s_node_group.inited) { (void) pthread_mutex_destroy(&s_node_group.mutex); node_group_reset(); } } static int nid_find(int nid, int *arr, int num) { int i; if (nid == INVALID_NID) { return (-1); } for (i = 0; i < num; i++) { if (arr[i] == nid) { return (i); } } return (-1); } static int cpuid_max_get(int *cpu_arr, int num) { int i, cpuid_max = -1; for (i = 0; i < num; i++) { if (cpuid_max < cpu_arr[i]) { cpuid_max = cpu_arr[i]; } } return (cpuid_max); } static int cpu_refresh(boolean_t init) { int i, j, num, cpuid_max = -1; int cpu_arr[NCPUS_NODE_MAX]; node_t *node; for (i = 0; i < NNODES_MAX; i++) { node = node_get(i); if (NODE_VALID(node)) { if (!os_sysfs_cpu_enum(node->nid, cpu_arr, NCPUS_NODE_MAX, &num)) { return (-1); } if (os_perf_cpuarr_refresh(node->cpus, NCPUS_NODE_MAX, cpu_arr, num, init) != 0) { return (-1); } node->ncpus = num; j = cpuid_max_get(cpu_arr, num); if (cpuid_max < j) { cpuid_max = j; } } } if (cpuid_max > s_node_group.cpuid_max) { s_node_group.cpuid_max = cpuid_max; } /* Refresh the number of online CPUs */ g_ncpus = os_sysfs_online_ncpus(); return (0); } static int meminfo_refresh(void) { int i; node_t *node; for (i = 0; i < NNODES_MAX; i++) { node = node_get(i); if (NODE_VALID(node)) { if (!os_sysfs_meminfo(node->nid, &node->meminfo)) { debug_print(NULL, 2, "meminfo_refresh:sysfs_meminfo failed\n"); return (-1); } } } return (0); } /* * Refresh the information of each node in group. The information * includes such as: CPU, physical memory size, free memory size. * Get the information from /sys/devices/system/node. */ int node_group_refresh(boolean_t init) { int node_arr[NNODES_MAX]; int num, i, j, ret = -1; node_t *node; node_group_lock(); if (!os_sysfs_node_enum(node_arr, NNODES_MAX, &num)) { goto L_EXIT; } for (i = 0; i < NNODES_MAX; i++) { node = node_get(i); if (NODE_VALID(node)) { if ((j = nid_find(node->nid, node_arr, num)) == -1) { node_hotremove(node); s_node_group.nnodes--; } else { node_arr[j] = INVALID_NID; } } } for (i = 0; i < NNODES_MAX; i++) { node = node_get(i); if (!NODE_VALID(node)) { if ((j = nid_find(i, node_arr, num)) >= 0) { ASSERT(node_arr[j] == i); if (init) { node_init(node, i, B_FALSE); } else { node_init(node, i, B_TRUE); } s_node_group.nnodes++; node_arr[j] = INVALID_NID; } } } if (cpu_refresh(init) != 0) { goto L_EXIT; } if (meminfo_refresh() != 0) { goto L_EXIT; } ret = 0; L_EXIT: node_group_unlock(); return (ret); } node_t * node_get(int nid) { return (&s_node_group.nodes[nid]); } int node_num(void) { return (s_node_group.nnodes); } void node_group_lock(void) { (void) pthread_mutex_lock(&s_node_group.mutex); } void node_group_unlock(void) { (void) pthread_mutex_unlock(&s_node_group.mutex); } /* * Get node by CPU id. */ node_t * node_by_cpu(int cpuid) { node_t *node; int i, j; if (cpuid == INVALID_CPUID) { return (NULL); } for (i = 0; i < NNODES_MAX; i++) { node = node_get(i); if (!NODE_VALID(node)) { continue; } for (j = 0; j < NCPUS_NODE_MAX; j++) { if (cpuid == node->cpus[j].cpuid) { return (node); } } } return (NULL); } int node_ncpus(node_t *node) { return (node->ncpus); } int node_intval_get(void) { return (s_node_group.intval_ms); } /* * Update the node's perf data. */ void node_countval_update(node_t *node, count_id_t count_id, uint64_t value) { node->countval.counts[count_id] += value; } /* * Return the perf data of specified node and count. */ uint64_t node_countval_get(node_t *node, count_id_t count_id) { return (node->countval.counts[count_id]); } /* * Return the memory information for a specified node. */ void node_meminfo(int nid, node_meminfo_t *info) { node_t *node; if (((node = node_get(nid)) != NULL) && (NODE_VALID(node))) { memcpy(info, &node->meminfo, sizeof (node_meminfo_t)); } } /* * Walk through the CPUs in a node and call 'func()' for each CPU. * The CPU might be INVALID. Note that the function could be only called * in perf thread. */ int node_cpu_traverse(pfn_perf_cpu_op_t func, void *arg, boolean_t err_ret, pfn_perf_cpu_op_t hotadd_func) { node_t *node; perf_cpu_t *cpu; int i, j, ret; for (i = 0; i < NNODES_MAX; i++) { node = node_get(i); if (!NODE_VALID(node)) { continue; } for (j = 0; j < NCPUS_NODE_MAX; j++) { cpu = &node->cpus[j]; if (cpu->hotremove) { pf_resource_free(cpu); cpu->hotremove = B_FALSE; cpu->cpuid = INVALID_CPUID; continue; } if ((cpu->hotadd) && (hotadd_func != NULL)) { hotadd_func(cpu, arg); cpu->hotadd = B_FALSE; } if ((func != NULL) && (cpu->cpuid != INVALID_CPUID) && (!cpu->hotadd)) { if (((ret = func(cpu, arg)) != 0) && (err_ret)) { return (ret); } } } if (node->hotremove) { node->nid = INVALID_NID; node->hotremove = B_FALSE; } } return (0); } uint64_t countval_sum(count_value_t *countval_arr, int cpuid_max, int nid, count_id_t count_id) { uint64_t value = 0; node_t *node; int i, cpuid, num = 0; node = node_get(nid); if (!NODE_VALID(node)) { return (0); } for (i = 0; i < NCPUS_NODE_MAX; i++) { if (num >= node->ncpus) { break; } if ((cpuid = node->cpus[i].cpuid) != INVALID_CPUID) { value += countval_arr[cpuid].counts[count_id]; num++; } } return (value); } uint64_t node_countval_sum(count_value_t *countval_arr, int cpuid_max, int nid, count_id_t count_id) { int i; uint64_t value = 0; if (nid != NODE_ALL) { return (countval_sum(countval_arr, cpuid_max, nid, count_id)); } for (i = 0; i < NNODES_MAX; i++) { value += countval_sum(countval_arr, cpuid_max, i, count_id); } return (value); } perf_cpu_t * node_cpus(node_t *node) { return (node->cpus); } void node_intval_update(int intval_ms) { s_node_group.intval_ms = intval_ms; } void node_profiling_clear(void) { node_t *node; int i; for (i = 0; i < NNODES_MAX; i++) { node = node_get(i); (void) memset(&node->countval, 0, sizeof (count_value_t)); } } node_t * node_valid_get(int node_idx) { int i, nvalid = 0; node_t *node; for (i = 0; i < NNODES_MAX; i++) { node = node_get(i); if (NODE_VALID(node)) { if (node_idx == nvalid) { return (node); } nvalid++; } } return (NULL); } int node_cpuid_max(void) { return (s_node_group.cpuid_max + 1); } numatop/common/os/sym.c0000664000175000017500000005074312633407552015014 0ustar jinyaojinyao/* * 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 the routines to process symbols in running process. */ #include #include #include #include #include #include #include "../include/util.h" #include "../include/proc.h" #include "../include/os/sym.h" #include "../include/os/map.h" static sym_lib_t *s_first_lib; static int elf32_binary_read(sym_binary_t *, sym_type_t); static int elf64_binary_read(sym_binary_t *, sym_type_t); static sym_ops_t s_sym_ops[SYM_CLASS_NUM] = { { elf32_binary_read }, { elf64_binary_read } }; void sym_init(void) { s_first_lib = NULL; } static void sym_binary_fini(sym_binary_t *binary) { if (binary->items != NULL) { free(binary->items); } if (binary->fp != NULL) { fclose(binary->fp); } memset(binary, 0, sizeof (sym_binary_t)); } void sym_fini(void) { sym_lib_t *p1, *p2 = s_first_lib; while (p2 != NULL) { p1 = p2->next; sym_binary_fini(&p2->binary); free(p2); p2 = p1; } s_first_lib = NULL; } static boolean_t magic_match(unsigned char *ident) { if ((ident[EI_MAG0] == ELFMAG0) && (ident[EI_MAG1] == ELFMAG1) && (ident[EI_MAG2] == ELFMAG2) && (ident[EI_MAG3] == ELFMAG3)) { return (B_TRUE); } return (B_FALSE); } static sym_class_t elf_class(FILE *fp) { unsigned char e_ident[EI_NIDENT]; if (fseek(fp, 0, SEEK_SET) != 0) { return (SYM_CLASS_INVALID); } if (fread(e_ident, EI_NIDENT, 1, fp) != 1) { return (SYM_CLASS_INVALID); } if (!magic_match(e_ident)) { return (SYM_CLASS_INVALID); } switch (e_ident[EI_CLASS]) { case ELFCLASS32: return (SYM_CLASS_ELF32); case ELFCLASS64: return (SYM_CLASS_ELF64); default: break; } return (SYM_CLASS_INVALID); } static uint64_t elf32_seg_loadaddr(FILE *fp, uint64_t phoff, int phnum, unsigned int flags) { Elf32_Phdr phdr; uint64_t load_addr = INVALID_LOADADDR; int i; if (fseek(fp, phoff, SEEK_SET) != 0) { goto L_EXIT; } for (i = 0; i < phnum; i++) { if (fread(&phdr, sizeof (Elf32_Phdr), 1, fp) != 1) { goto L_EXIT; } if ((phdr.p_type == PT_LOAD) && ((phdr.p_flags & flags) != 0)) { break; } } if (i == phnum) { goto L_EXIT; } load_addr = phdr.p_vaddr; L_EXIT: return (load_addr); } static uint64_t elf64_seg_loadaddr(FILE *fp, uint64_t phoff, int phnum, unsigned int flags) { Elf64_Phdr phdr; uint64_t load_addr = INVALID_LOADADDR; int i; if (fseek(fp, phoff, SEEK_SET) != 0) { goto L_EXIT; } for (i = 0; i < phnum; i++) { if (fread(&phdr, sizeof (Elf64_Phdr), 1, fp) != 1) { goto L_EXIT; } if ((phdr.p_type == PT_LOAD) && ((phdr.p_flags & flags) != 0)) { break; } } if (i == phnum) { goto L_EXIT; } load_addr = phdr.p_vaddr; L_EXIT: return (load_addr); } static int sym_item_add(sym_binary_t *binary, char *sym_name, uint64_t sym_addr, uint64_t sym_size, sym_type_t sym_type, uint64_t load_addr) { sym_item_t *item; if (array_alloc((void **)&binary->items, &binary->nitem_cur, &binary->nitem_max, sizeof (sym_item_t), SYM_ITEM_NUM) != 0) { return (-1); } item = &binary->items[binary->nitem_cur]; strncpy(item->name, sym_name, SYM_NAME_SIZE); item->name[SYM_NAME_SIZE - 1] = 0; item->type = sym_type; item->off = sym_addr - load_addr; item->size = sym_size; binary->nitem_cur++; return (0); } static int elf32_symtab_copyout(sym_binary_t *binary, uint64_t symtab_off, int symtab_num, uint64_t strtab_off, uint64_t strtab_size, uint64_t load_addr, sym_type_t sym_type) { Elf32_Sym sym; char *strtab; int i, ret = -1; if (symtab_num == 0) { return (0); } if ((strtab = zalloc(strtab_size + 1)) == NULL) { return (-1); } if (fseek(binary->fp, strtab_off, SEEK_SET) != 0) { goto L_EXIT; } if (fread(strtab, strtab_size, 1, binary->fp) != 1) { goto L_EXIT; } if (fseek(binary->fp, symtab_off, SEEK_SET) != 0) { goto L_EXIT; } for (i = 0; i < symtab_num; i++) { if (fread(&sym, sizeof (Elf32_Sym), 1, binary->fp) != 1) { goto L_EXIT; } if (sym.st_size == 0) { continue; } switch (sym_type) { case SYM_TYPE_FUNC: if ((ELF32_ST_TYPE(sym.st_info) == STT_FUNC) || (ELF32_ST_TYPE(sym.st_info) == STT_IFUNC)) { if (sym_item_add(binary, strtab + sym.st_name, sym.st_value, sym.st_size, sym_type, load_addr) != 0) { goto L_EXIT; } } break; default: break; } } ret = 0; L_EXIT: free(strtab); return (ret); } static int elf64_symtab_copyout(sym_binary_t *binary, uint64_t symtab_off, int symtab_num, uint64_t strtab_off, uint64_t strtab_size, uint64_t load_addr, sym_type_t sym_type) { Elf64_Sym sym; char *strtab; int i, ret = -1; if (symtab_num == 0) { return (0); } if ((strtab = zalloc(strtab_size + 1)) == NULL) { return (-1); } if (fseek(binary->fp, strtab_off, SEEK_SET) != 0) { goto L_EXIT; } if (fread(strtab, strtab_size, 1, binary->fp) != 1) { goto L_EXIT; } if (fseek(binary->fp, symtab_off, SEEK_SET) != 0) { goto L_EXIT; } for (i = 0; i < symtab_num; i++) { if (fread(&sym, sizeof (Elf64_Sym), 1, binary->fp) != 1) { goto L_EXIT; } if (sym.st_size == 0) { continue; } switch (sym_type) { case SYM_TYPE_FUNC: if ((ELF64_ST_TYPE(sym.st_info) == STT_FUNC) || (ELF64_ST_TYPE(sym.st_info) == STT_IFUNC)) { if (sym_item_add(binary, strtab + sym.st_name, sym.st_value, sym.st_size, sym_type, load_addr) != 0) { goto L_EXIT; } } break; default: break; } } ret = 0; L_EXIT: free(strtab); return (ret); } static int sym_cmp(const void *a, const void *b) { sym_item_t *s1 = (sym_item_t *)a; sym_item_t *s2 = (sym_item_t *)b; if (s1->off < s2->off) { return (-1); } if (s1->off > s2->off) { return (1); } return (0); } static void item_sort(sym_binary_t *binary) { int i; qsort(binary->items, binary->nitem_cur, sizeof (sym_item_t), sym_cmp); for (i = 0; i < binary->nitem_cur; i++) { binary->items[i].index = i; } } static int elf32_binary_read(sym_binary_t *binary, sym_type_t sym_type) { Elf32_Ehdr ehdr; Elf32_Shdr shdr; char *shstrtab; uint64_t load_addr; uint64_t symtab_off = 0, strtab_off = 0, strtab_size = 0; int symtab_num = 0, i, ret = -1; if (fseek(binary->fp, 0, SEEK_SET) != 0) { return (-1); } if (fread(&ehdr, sizeof (Elf32_Ehdr), 1, binary->fp) != 1) { return (-1); } if (ehdr.e_shentsize != sizeof (Elf32_Shdr)) { debug_print(NULL, 2, "elf32_binary_read: ehdr.e_shentsize != %d\n", sizeof (Elf32_Shdr)); return (-1); } /* * Get the load address of "x" segment. */ if ((load_addr = elf32_seg_loadaddr(binary->fp, ehdr.e_phoff, ehdr.e_phnum, PF_X)) == INVALID_LOADADDR) { return (-1); } /* * Read the "shstrtab" to a buffer. */ if (fseek(binary->fp, ehdr.e_shoff + ehdr.e_shstrndx * sizeof (Elf32_Shdr), SEEK_SET) != 0) { return (-1); } if (fread(&shdr, sizeof (Elf32_Shdr), 1, binary->fp) != 1) { return (-1); } if ((shstrtab = zalloc(shdr.sh_size)) == NULL) { return (-1); } if (fseek(binary->fp, shdr.sh_offset, SEEK_SET) != 0) { goto L_EXIT; } if (fread(shstrtab, shdr.sh_size, 1, binary->fp) != 1) { goto L_EXIT; } /* * Move to the start of sections. */ if (fseek(binary->fp, ehdr.e_shoff, SEEK_SET) != 0) { goto L_EXIT; } /* * Walk on each section. */ for (i = 0; i < ehdr.e_shnum; i++) { if (fread(&shdr, sizeof (Elf32_Shdr), 1, binary->fp) != 1) { goto L_EXIT; } if ((strcmp(shstrtab + shdr.sh_name, ".symtab") == 0) || (strcmp(shstrtab + shdr.sh_name, ".dynsym") == 0)) { symtab_off = shdr.sh_offset; symtab_num = (int)(shdr.sh_size / shdr.sh_entsize); } if ((strcmp(shstrtab + shdr.sh_name, ".strtab") == 0) || (strcmp(shstrtab + shdr.sh_name, ".dynstr") == 0)) { strtab_off = shdr.sh_offset; strtab_size = shdr.sh_size; } } if (elf32_symtab_copyout(binary, symtab_off, symtab_num, strtab_off, strtab_size, load_addr, sym_type) != 0) { goto L_EXIT; } item_sort(binary); ret = 0; L_EXIT: free(shstrtab); return (ret); } static int elf64_binary_read(sym_binary_t *binary, unsigned int sym_type) { Elf64_Ehdr ehdr; Elf64_Shdr shdr; char *shstrtab; uint64_t load_addr; uint64_t symtab_off = 0, strtab_off = 0, strtab_size = 0; int symtab_num = 0, i, ret = -1; if (fseek(binary->fp, 0, SEEK_SET) != 0) { return (-1); } if (fread(&ehdr, sizeof (Elf64_Ehdr), 1, binary->fp) != 1) { return (-1); } if (ehdr.e_shentsize != sizeof (Elf64_Shdr)) { debug_print(NULL, 2, "elf64_binary_read: ehdr.e_shentsize != %d\n", sizeof (Elf64_Shdr)); return (-1); } /* * Get the load address of "x" segment. */ if ((load_addr = elf64_seg_loadaddr(binary->fp, ehdr.e_phoff, ehdr.e_phnum, PF_X)) == INVALID_LOADADDR) { return (-1); } /* * Read the "shstrtab" to a buffer. */ if (fseek(binary->fp, ehdr.e_shoff + ehdr.e_shstrndx * sizeof (Elf64_Shdr), SEEK_SET) != 0) { return (-1); } if (fread(&shdr, sizeof (Elf64_Shdr), 1, binary->fp) != 1) { return (-1); } if ((shstrtab = zalloc(shdr.sh_size)) == NULL) { return (-1); } if (fseek(binary->fp, shdr.sh_offset, SEEK_SET) != 0) { goto L_EXIT; } if (fread(shstrtab, shdr.sh_size, 1, binary->fp) != 1) { goto L_EXIT; } /* * Move to the start of sections. */ if (fseek(binary->fp, ehdr.e_shoff, SEEK_SET) != 0) { goto L_EXIT; } /* * Walk on each section. */ for (i = 0; i < ehdr.e_shnum; i++) { if (fread(&shdr, sizeof (Elf64_Shdr), 1, binary->fp) != 1) { goto L_EXIT; } if ((strcmp(shstrtab + shdr.sh_name, ".symtab") == 0) || (strcmp(shstrtab + shdr.sh_name, ".dynsym") == 0)) { symtab_off = shdr.sh_offset; symtab_num = (int)(shdr.sh_size / shdr.sh_entsize); } if ((strcmp(shstrtab + shdr.sh_name, ".strtab") == 0) || (strcmp(shstrtab + shdr.sh_name, ".dynstr") == 0)) { strtab_off = shdr.sh_offset; strtab_size = shdr.sh_size; } } if (elf64_symtab_copyout(binary, symtab_off, symtab_num, strtab_off, strtab_size, load_addr, sym_type) != 0) { goto L_EXIT; } item_sort(binary); ret = 0; L_EXIT: free(shstrtab); return (ret); } static int binary_sym_read(sym_binary_t *binary, sym_type_t sym_type) { sym_class_t cls; int ret = -1; if ((binary->fp = fopen(binary->path, "r")) == NULL) { return (-1); } if ((cls = elf_class(binary->fp)) == SYM_CLASS_INVALID) { goto L_EXIT; } if ((s_sym_ops[cls].pfn_binary_read)(binary, sym_type) != 0) { goto L_EXIT; } ret = 0; L_EXIT: fclose(binary->fp); binary->fp = NULL; return (ret); } static int image_sym_read(sym_t *sym, map_entry_t *map, sym_type_t sym_type) { sym_binary_t *binary = &sym->image; strncpy(binary->path, map->desc, PATH_MAX); binary->path[PATH_MAX - 1] = 0; if (binary_sym_read(binary, sym_type) != 0) { return (-1); } if (MAP_X(map->attr)) { sym->image_loadaddr = map->start_addr; } return (0); } static sym_lib_t * lib_find(char *path) { sym_lib_t *p = s_first_lib; while (p != NULL) { if (strcmp(p->binary.path, path) == 0) { return (p); } p = p->next; } return (NULL); } static sym_lib_t * lib_add(char *path, sym_type_t sym_type) { sym_lib_t *lib, *p; sym_binary_t *binary; if ((lib = zalloc(sizeof (sym_lib_t))) == NULL) { return (NULL); } binary = &lib->binary; strncpy(binary->path, path, PATH_MAX); binary->path[PATH_MAX - 1] = 0; if (binary_sym_read(binary, sym_type) != 0) { free(lib); return (NULL); } p = s_first_lib; s_first_lib = lib; lib->next = p; return (lib); } static int libref_add(sym_libref_t *ref, sym_lib_t *lib, map_entry_t *map) { int i, j; i = ref->nlib_cur; j = ref->nlib_max; if (array_alloc((void **)(&ref->libs), &ref->nlib_cur, &ref->nlib_max, sizeof (uint64_t), SYM_LIB_NUM) != 0) { return (-1); } if (array_alloc((void **)(&ref->lib_loadaddr), &i, &j, sizeof (uint64_t), SYM_LIB_NUM) != 0) { return (-1); } i = ref->nlib_cur; ref->libs[i] = lib; ref->lib_loadaddr[i] = map->start_addr; ref->nlib_cur++; return (0); } static void libref_free(sym_libref_t *ref) { if (ref->libs != NULL) { free(ref->libs); } if (ref->lib_loadaddr != NULL) { free(ref->lib_loadaddr); } } static int lib_sym_read(sym_t *sym, map_entry_t *map, sym_type_t sym_type) { sym_lib_t *lib; if ((lib = lib_find(map->desc)) == NULL) { if ((lib = lib_add(map->desc, sym_type)) == NULL) { return (-1); } } if (libref_add(&sym->libref, lib, map) != 0) { return (-1); } return (0); } int sym_load(track_proc_t *proc, sym_type_t sym_type) { map_proc_t *map; map_entry_t *entry; int i; if (map_proc_load(proc) != 0) { return (-1); } map = &proc->map; for (i = 0; i < map->nentry_cur; i++) { entry = &map->arr[i]; if ((entry->need_resolve) && (MAP_X(entry->attr)) && (sym_type == SYM_TYPE_FUNC)) { if (strstr(entry->desc, ".so") != NULL) { lib_sym_read(&proc->sym, entry, sym_type); } else { image_sym_read(&proc->sym, entry, sym_type); } entry->need_resolve = B_FALSE; } } proc->sym.loaded = B_TRUE; return (0); } void sym_free(sym_t *sym) { if (sym->loaded) { sym_binary_fini(&sym->image); libref_free(&sym->libref); sym->loaded = B_FALSE; } } static int off_cmp(const void *a, const void *b) { uint64_t off = *(uint64_t *)a; sym_item_t *item = (sym_item_t *)b; if (off >= item->off + item->size) { return (1); } if (off < item->off) { return (-1); } return (0); } static sym_item_t * resolve(sym_binary_t *binary, uint64_t off, int *num) { sym_item_t *item, *arr; int i, j, start; if ((item = bsearch(&off, (void *)(binary->items), binary->nitem_cur, sizeof (sym_item_t), off_cmp)) == NULL) { return (NULL); } /* * Multiple symbols could be mapped to same address. */ *num = 1; start = item->index; i = item->index - 1; while (i >= 0) { if (binary->items[i].off == off) { (*num)++; start = i; i--; } else { break; } } i = item->index + 1; while (i < binary->nitem_cur) { if (binary->items[i].off == off) { (*num)++; i++; } else { break; } } if ((arr = zalloc(sizeof (sym_item_t) * (*num))) == NULL) { return (NULL); } j = 0; for (i = start; i < start + *num; i++) { arr[j] = binary->items[i]; j++; } return (arr); } static int sym_resolve(sym_t *sym, uint64_t addr, sym_item_t **item_arr, int *num, uint64_t *base_addr) { sym_libref_t *libref; sym_binary_t *binary; int i; if (!sym->loaded) { return (-1); } if ((*item_arr = resolve(&sym->image, addr - sym->image_loadaddr, num)) != NULL) { *base_addr = sym->image_loadaddr; return (0); } libref = &sym->libref; for (i = 0; i < libref->nlib_cur; i++) { binary = &((libref->libs[i])->binary); if ((*item_arr = resolve(binary, addr - libref->lib_loadaddr[i], num)) != NULL) { *base_addr = libref->lib_loadaddr[i]; return (0); } } return (-1); } static sym_item_t * resolve_unique(sym_t *sym, uint64_t addr, sym_item_t **arr, uint64_t *base_addr) { sym_item_t *item_arr, *item; int num, i; if (sym_resolve(sym, addr, &item_arr, &num, base_addr) != 0) { *arr = NULL; return (NULL); } ASSERT(num > 0); for (i = 0; i < num; i++) { item = &item_arr[i]; if (item->name[0] != '_') { *arr = item_arr; return (item); } } *arr = item_arr; return (item); } static boolean_t ips_exist(sym_callchain_t *chain, uint64_t *ips, int ips_num) { int i; if (ips_num > chain->nentry) { return (B_FALSE); } for (i = 0; i < ips_num; i++) { if (!IP_HIT(ips[i], chain->entry_arr[i].addr, chain->entry_arr[i].size)) { return (B_FALSE); } } return (B_TRUE); } static sym_callchain_t * chain_find(sym_chainlist_t *list, uint64_t *ips, int ips_num) { sym_callchain_t *chain; chain = list->head; while (chain != NULL) { if (ips_exist(chain, ips, ips_num)) { return (chain); } chain = chain->next; } return (NULL); } static sym_callchain_t * chain_alloc(sym_t *sym, uint64_t *ips, int ips_num) { sym_callentry_t *entry_arr, *entry; sym_item_t *item_arr, *item; sym_callchain_t *chain; uint64_t base_addr; int i; if ((entry_arr = zalloc(ips_num * sizeof (sym_callentry_t))) == NULL) { return (NULL); } for (i = 0; i < ips_num; i++) { entry = &entry_arr[i]; if ((item = resolve_unique(sym, ips[i], &item_arr, &base_addr)) == NULL) { /* * Can't resolve the symbol, just record the address. */ entry->addr = ips[i]; entry->size = sizeof (ips[i]); snprintf(entry->name, SYM_NAME_SIZE, "0x%llx", (long long)ips[i]); } else { /* * Got the symbol. */ entry->addr = base_addr + item->off; entry->size = item->size; strncpy(entry->name, item->name, SYM_NAME_SIZE); entry->name[SYM_NAME_SIZE - 1] = 0; free(item_arr); } } if ((chain = zalloc(sizeof (sym_callchain_t))) == NULL) { free(entry_arr); return (NULL); } chain->entry_arr = entry_arr; chain->nentry = ips_num; return (chain); } static void chainlist_attach_tail(sym_chainlist_t *list, sym_callchain_t *chain) { sym_callchain_t *tail = list->tail; chain->next = NULL; chain->prev = tail; if (tail != NULL) { tail->next = chain; } else { list->head = chain; } list->tail = chain; list->num++; } static void chainlist_detach(sym_chainlist_t *list, sym_callchain_t *chain) { sym_callchain_t *prev, *next; prev = chain->prev; next = chain->next; if (prev != NULL) { prev->next = next; } else { list->head = next; } if (next != NULL) { next->prev = prev; } else { list->tail = prev; } chain->prev = NULL; chain->next = NULL; list->num--; } int sym_callchain_add(sym_t *sym, uint64_t *ips, int ips_num, sym_chainlist_t *list) { sym_callchain_t *chain; if ((chain = chain_find(list, ips, ips_num)) != NULL) { chain->naccess++; return (0); } if ((chain = chain_alloc(sym, ips, ips_num)) == NULL) { return (-1); } chainlist_attach_tail(list, chain); return (0); } static sym_callchain_t * max_access_chain(sym_chainlist_t *list) { sym_callchain_t *p = list->head, *found = NULL; int nmax = -1; while (p != NULL) { if (p->naccess > nmax) { nmax = p->naccess; found = p; } p = p->next; } return (found); } void sym_callchain_resort(sym_chainlist_t *list) { sym_chainlist_t sortlist; sym_callchain_t *p; memset(&sortlist, 0, sizeof (sym_chainlist_t)); while (list->num > 0) { if ((p = max_access_chain(list)) == NULL) { break; } /* * Remove the found node from list */ chainlist_detach(list, p); chainlist_attach_tail(&sortlist, p); } sym_chainlist_free(list); memcpy(list, &sortlist, sizeof (sym_chainlist_t)); } sym_callchain_t * sym_callchain_detach(sym_chainlist_t *list) { sym_callchain_t *chain; if ((chain = list->head) != NULL) { chainlist_detach(list, chain); } return (chain); } void sym_callchain_free(sym_callchain_t *chain) { if (chain->entry_arr != NULL) { free(chain->entry_arr); } free(chain); } void sym_chainlist_free(sym_chainlist_t *list) { sym_callchain_t *next, *p; p = list->head; while (p != NULL) { next = p->next; sym_callchain_free(p); p = next; } memset(list, 0, sizeof (sym_chainlist_t)); } int sym_chainlist_nentry(sym_chainlist_t *list, int *nchain) { sym_callchain_t *next, *p; int nentry = 0; *nchain = 0; p = list->head; while (p != NULL) { next = p->next; *nchain += 1; nentry += p->nentry; p = next; } return (nentry); } numatop/common/os/os_perf.c0000664000175000017500000004771212633407552015643 0ustar jinyaojinyao/* * 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 #include #include #include #include #include "../include/types.h" #include "../include/proc.h" #include "../include/lwp.h" #include "../include/util.h" #include "../include/disp.h" #include "../include/perf.h" #include "../include/os/pfwrapper.h" #include "../include/os/node.h" #include "../include/os/plat.h" #include "../include/os/os_perf.h" typedef struct _profiling_conf { pf_conf_t conf_arr[COUNT_NUM]; } profiling_conf_t; static pf_profiling_rec_t *s_profiling_recbuf = NULL; static pf_ll_rec_t *s_ll_recbuf = NULL; static profiling_conf_t s_profiling_conf; static pf_conf_t s_ll_conf; static boolean_t s_partpause_enabled; static boolean_t event_valid(perf_cpu_t *cpu) { return (cpu->map_base != MAP_FAILED); } static void cpu_op(perf_cpu_t *cpu, pfn_pf_event_op_t op) { if (!event_valid(cpu)) { return; } if (op(cpu) != 0) { /* * ioctl is failed. It might be the CPU is offline or other * error occurred. */ pf_resource_free(cpu); } } static void cpu_init(perf_cpu_t *cpu) { int i; for (i = 0; i < COUNT_NUM; i++) { cpu->fds[i] = INVALID_FD; } cpu->map_base = MAP_FAILED; } static int cpu_profiling_setup(perf_cpu_t *cpu, void *arg) { pf_conf_t *conf_arr = s_profiling_conf.conf_arr; int i, ret = 0; cpu_init(cpu); for (i = 0; i < COUNT_NUM; i++) { if (conf_arr[i].config == INVALID_CONFIG) { /* * Invalid config is at the end of array. */ break; } if (pf_profiling_setup(cpu, i, &conf_arr[i]) != 0) { ret = -1; break; } } if (ret != 0) { pf_resource_free(cpu); } return (ret); } static int cpu_profiling_start(perf_cpu_t *cpu, void *arg) { cpu_op(cpu, pf_profiling_allstart); return (0); } static int cpu_profiling_stop(perf_cpu_t *cpu, void *arg) { cpu_op(cpu, pf_profiling_allstop); return (0); } static int cpu_resource_free(perf_cpu_t *cpu, void *arg) { pf_resource_free(cpu); return (0); } static void countval_diff_base(perf_cpu_t *cpu, pf_profiling_rec_t *record) { count_value_t *countval_last = &cpu->countval_last; count_value_t *countval_new = &record->countval; int i; for (i = 0; i < COUNT_NUM; i++) { countval_last->counts[i] = countval_new->counts[i]; } } static void countval_diff(perf_cpu_t *cpu, pf_profiling_rec_t *record, count_value_t *diff) { count_value_t *countval_last = &cpu->countval_last; count_value_t *countval_new = &record->countval; int i; for (i = 0; i < COUNT_NUM; i++) { if (countval_last->counts[i] <= countval_new->counts[i]) { diff->counts[i] = countval_new->counts[i] - countval_last->counts[i]; } else { /* Something wrong, discard it, */ diff->counts[i] = 0; } countval_last->counts[i] = countval_new->counts[i]; } } static int chain_add(perf_countchain_t *count_chain, int count_id, uint64_t count_value, uint64_t *ips, int ip_num) { perf_chainrecgrp_t *grp; perf_chainrec_t *rec; grp = &count_chain->chaingrps[count_id]; if (array_alloc((void **)&grp->rec_arr, &grp->nrec_cur, &grp->nrec_max, sizeof (perf_chainrec_t), PERF_REC_NUM) != 0) { return (-1); } rec = &(grp->rec_arr[grp->nrec_cur]); rec->count_value = count_value; rec->callchain.ip_num = ip_num; memcpy(rec->callchain.ips, ips, IP_NUM * sizeof (uint64_t)); grp->nrec_cur++; return (0); } static int cpu_profiling_smpl(perf_cpu_t *cpu, void *arg) { pf_profiling_rec_t *record; track_proc_t *proc; track_lwp_t *lwp; node_t *node; count_value_t diff; int i, j, record_num; if (!event_valid(cpu)) { return (0); } /* * The record is grouped by pid/tid. */ pf_profiling_record(cpu, s_profiling_recbuf, &record_num); if (record_num == 0) { return (0); } if ((node = node_by_cpu(cpu->cpuid)) == NULL) { return (0); } countval_diff_base(cpu, &s_profiling_recbuf[0]); for (i = 1; i < record_num; i++) { record = &s_profiling_recbuf[i]; countval_diff(cpu, record, &diff); if ((proc = proc_find(record->pid)) == NULL) { return (0); } if ((lwp = proc_lwp_find(proc, record->tid)) == NULL) { proc_refcount_dec(proc); return (0); } pthread_mutex_lock(&proc->mutex); for (j = 0; j < COUNT_NUM; j++) { if (!s_partpause_enabled) { proc_countval_update(proc, cpu->cpuid, j, diff.counts[j]); lwp_countval_update(lwp, cpu->cpuid, j, diff.counts[j]); node_countval_update(node, j, diff.counts[j]); } if ((record->ip_num > 0) && (diff.counts[j] >= g_sample_period[j][g_precise])) { /* * The event is overflowed. The call-chain represents * the context when event is overflowed. */ chain_add(&proc->count_chain, j, diff.counts[j], record->ips, record->ip_num); chain_add(&lwp->count_chain, j, diff.counts[j], record->ips, record->ip_num); } } pthread_mutex_unlock(&proc->mutex); lwp_refcount_dec(lwp); proc_refcount_dec(proc); } return (0); } static int cpu_profiling_setupstart(perf_cpu_t *cpu, void *arg) { if (cpu_profiling_setup(cpu, NULL) != 0) { return (-1); } return (cpu_profiling_start(cpu, NULL)); } static int cpu_profiling_partpause(perf_cpu_t *cpu, void *arg) { count_id_t count_id = (count_id_t)arg; int i; if ((count_id == COUNT_INVALID) || (count_id == 0)) { return (pf_profiling_allstop(cpu)); } for (i = 1; i < COUNT_NUM; i++) { if (i != count_id) { pf_profiling_stop(cpu, i); } else { pf_profiling_start(cpu, i); } } return (0); } static int cpu_profiling_restore(perf_cpu_t *cpu, void *arg) { count_id_t count_id = (count_id_t)arg; int i; if ((count_id == COUNT_INVALID) || (count_id == 0)) { return (pf_profiling_allstart(cpu)); } pf_profiling_stop(cpu, count_id); /* * Discard the existing records in ring buffer. */ pf_profiling_record(cpu, NULL, NULL); for (i = 1; i < COUNT_NUM; i++) { pf_profiling_start(cpu, i); } return (0); } static int cpu_ll_setup(perf_cpu_t *cpu, void *arg) { cpu_init(cpu); if (pf_ll_setup(cpu, &s_ll_conf) != 0) { pf_resource_free(cpu); return (-1); } return (0); } static int cpu_ll_start(perf_cpu_t *cpu, void *arg) { cpu_op(cpu, pf_ll_start); return (0); } static int cpu_ll_stop(perf_cpu_t *cpu, void *arg) { cpu_op(cpu, pf_ll_stop); return (0); } int llrec_add(perf_llrecgrp_t *grp, pf_ll_rec_t *record) { os_perf_llrec_t *llrec; if (array_alloc((void **)(&grp->rec_arr), &grp->nrec_cur, &grp->nrec_max, sizeof (os_perf_llrec_t), PERF_REC_NUM) != 0) { return (-1); } llrec = &(grp->rec_arr[grp->nrec_cur]); llrec->addr = record->addr; llrec->cpu = record->cpu; llrec->latency = record->latency; llrec->callchain.ip_num = record->ip_num; memcpy(llrec->callchain.ips, record->ips, IP_NUM * sizeof (uint64_t)); grp->nrec_cur++; return (0); } static int cpu_ll_smpl(perf_cpu_t *cpu, void *arg) { task_ll_t *task = (task_ll_t *)arg; pf_ll_rec_t *record; track_proc_t *proc; track_lwp_t *lwp; int record_num, i; pf_ll_record(cpu, s_ll_recbuf, &record_num); if (record_num == 0) { return (0); } for (i = 0; i < record_num; i++) { record = &s_ll_recbuf[i]; if ((task->pid != 0) && (task->pid != record->pid)) { continue; } if ((task->pid != 0) && (task->lwpid != 0) && (task->lwpid != record->tid)) { continue; } if ((proc = proc_find(record->pid)) == NULL) { return (0); } if ((lwp = proc_lwp_find(proc, record->tid)) == NULL) { proc_refcount_dec(proc); return (0); } pthread_mutex_lock(&proc->mutex); llrec_add(&proc->llrec_grp, record); llrec_add(&lwp->llrec_grp, record); pthread_mutex_unlock(&proc->mutex); lwp_refcount_dec(lwp); proc_refcount_dec(proc); } return (0); } static int cpu_ll_setupstart(perf_cpu_t *cpu, void *arg) { if (cpu_ll_setup(cpu, NULL) != 0) { return (-1); } return (cpu_ll_start(cpu, NULL)); } static int profiling_pause(void) { node_cpu_traverse(cpu_profiling_stop, NULL, B_FALSE, NULL); return (0); } static int profiling_stop(void) { profiling_pause(); node_cpu_traverse(cpu_resource_free, NULL, B_FALSE, NULL); return (0); } static int profiling_start(perf_ctl_t *ctl, task_profiling_t *task) { /* Setup perf on each CPU. */ if (node_cpu_traverse(cpu_profiling_setup, NULL, B_TRUE, NULL) != 0) { return (-1); } profiling_pause(); /* Start to count on each CPU. */ if (node_cpu_traverse(cpu_profiling_start, NULL, B_TRUE, NULL) != 0) { return (-1); } ctl->last_ms = current_ms(); return (0); } static int profiling_smpl(perf_ctl_t *ctl, task_profiling_t *task, int *intval_ms) { *intval_ms = current_ms() - ctl->last_ms; proc_intval_update(*intval_ms); node_intval_update(*intval_ms); node_cpu_traverse(cpu_profiling_smpl, NULL, B_FALSE, cpu_profiling_setupstart); ctl->last_ms = current_ms(); return (0); } static int profiling_partpause(perf_ctl_t *ctl, task_partpause_t *task) { node_cpu_traverse(cpu_profiling_partpause, (void *)(task->count_id), B_FALSE, NULL); s_partpause_enabled = B_TRUE; return (0); } static int profiling_restore(perf_ctl_t *ctl, task_restore_t *task) { node_cpu_traverse(cpu_profiling_restore, (void *)(task->count_id), B_FALSE, NULL); s_partpause_enabled = B_FALSE; ctl->last_ms = current_ms(); return (0); } static int ll_start(perf_ctl_t *ctl) { /* Setup perf on each CPU. */ if (node_cpu_traverse(cpu_ll_setup, NULL, B_TRUE, NULL) != 0) { return (-1); } /* Start to count on each CPU. */ node_cpu_traverse(cpu_ll_start, NULL, B_FALSE, NULL); ctl->last_ms = current_ms(); return (0); } static int ll_stop(void) { node_cpu_traverse(cpu_ll_stop, NULL, B_FALSE, NULL); node_cpu_traverse(cpu_resource_free, NULL, B_FALSE, NULL); return (0); } static int ll_smpl(perf_ctl_t *ctl, task_ll_t *task, int *intval_ms) { *intval_ms = current_ms() - ctl->last_ms; proc_intval_update(*intval_ms); node_cpu_traverse(cpu_ll_smpl, (void *)task, B_FALSE, cpu_ll_setupstart); ctl->last_ms = current_ms(); return (0); } boolean_t os_profiling_started(perf_ctl_t *ctl) { if ((ctl->status == PERF_STATUS_PROFILING_PART_STARTED) || (ctl->status == PERF_STATUS_PROFILING_STARTED)) { return (B_TRUE); } return (B_FALSE); } /* ARGSUSED */ int os_profiling_start(perf_ctl_t *ctl, perf_task_t *task) { if (perf_profiling_started()) { perf_status_set(PERF_STATUS_PROFILING_STARTED); debug_print(NULL, 2, "profiling started yet\n"); return (0); } os_allstop(); proc_ll_clear(NULL); if (profiling_start(ctl, (task_profiling_t *)(task)) != 0) { exit_msg_put("Fail to setup perf (probably permission denied)!\n"); debug_print(NULL, 2, "os_profiling_start failed\n"); perf_status_set(PERF_STATUS_PROFILING_FAILED); return (-1); } debug_print(NULL, 2, "os_profiling_start success\n"); perf_status_set(PERF_STATUS_PROFILING_STARTED); return (0); } int os_profiling_smpl(perf_ctl_t *ctl, perf_task_t *task, int *intval_ms) { if (!perf_profiling_started()) { return (-1); } proc_enum_update(0); proc_callchain_clear(); proc_profiling_clear(); node_profiling_clear(); if (profiling_smpl(ctl, (task_profiling_t *)task, intval_ms) != 0) { perf_status_set(PERF_STATUS_PROFILING_FAILED); disp_profiling_data_fail(); return (-1); } disp_profiling_data_ready(*intval_ms); return (0); } int os_profiling_partpause(perf_ctl_t *ctl, perf_task_t *task) { profiling_partpause(ctl, (task_partpause_t *)(task)); perf_status_set(PERF_STATUS_PROFILING_PART_STARTED); return (0); } int os_profiling_restore(perf_ctl_t *ctl, perf_task_t *task) { proc_callchain_clear(); proc_profiling_clear(); profiling_restore(ctl, (task_restore_t *)(task)); perf_status_set(PERF_STATUS_PROFILING_STARTED); return (0); } int os_callchain_start(perf_ctl_t *ctl, perf_task_t *task) { /* Not supported in Linux. */ return (0); } int os_callchain_smpl(perf_ctl_t *ctl, perf_task_t *task, int *intval_ms) { /* Not supported in Linux. */ return (0); } int os_ll_start(perf_ctl_t *ctl, perf_task_t *task) { os_allstop(); proc_callchain_clear(); proc_profiling_clear(); node_profiling_clear(); if (ll_start(ctl) != 0) { /* * It could be failed if the kernel doesn't support PEBS LL. */ debug_print(NULL, 2, "ll_start is failed\n"); perf_status_set(PERF_STATUS_LL_FAILED); return (-1); } debug_print(NULL, 2, "ll_start success\n"); perf_status_set(PERF_STATUS_LL_STARTED); return (0); } int os_ll_smpl(perf_ctl_t *ctl, perf_task_t *task, int *intval_ms) { if (!perf_ll_started()) { return (-1); } proc_enum_update(0); proc_ll_clear(0); if (ll_smpl(ctl, (task_ll_t *)(task), intval_ms) != 0) { perf_status_set(PERF_STATUS_LL_FAILED); disp_ll_data_fail(); return (-1); } disp_ll_data_ready(*intval_ms); return (0); } static void profiling_init(profiling_conf_t *conf) { plat_event_config_t cfg; pf_conf_t *conf_arr = conf->conf_arr; int i; for (i = 0; i < COUNT_NUM; i++) { plat_profiling_config(i, &cfg); conf_arr[i].count_id = i; conf_arr[i].type = cfg.type; switch (conf_arr[i].type) { case PERF_TYPE_RAW: if (cfg.config != INVALID_CODE_UMASK) { conf_arr[i].config = (cfg.config) | (cfg.other_attr << 16); } else { conf_arr[i].config = INVALID_CONFIG; } break; case PERF_TYPE_HARDWARE: conf_arr[i].config = cfg.config; break; default: break; } conf_arr[i].config1 = cfg.extra_value; conf_arr[i].sample_period = g_sample_period[i][g_precise]; } } static void ll_init(pf_conf_t *conf) { plat_event_config_t cfg; plat_ll_config(&cfg); conf->count_id = COUNT_INVALID; conf->type = cfg.type; conf->config = (cfg.config) | (cfg.other_attr << 16); conf->config1 = cfg.extra_value; conf->sample_period = LL_PERIOD; } int os_perf_init(void) { int ringsize, size; s_profiling_recbuf = NULL; s_ll_recbuf = NULL; s_partpause_enabled = B_FALSE; ringsize = pf_ringsize_init(); size = ((ringsize / sizeof (pf_profiling_rbrec_t)) + 1) * sizeof (pf_profiling_rec_t); if ((s_profiling_recbuf = zalloc(size)) == NULL) { return (-1); } profiling_init(&s_profiling_conf); size = ((ringsize / sizeof (pf_ll_rbrec_t)) + 1) * sizeof (pf_ll_rec_t); if ((s_ll_recbuf = zalloc(size)) == NULL) { free(s_profiling_recbuf); s_profiling_recbuf = NULL; return (-1); } ll_init(&s_ll_conf); return (0); } void os_perf_fini(void) { if (s_profiling_recbuf != NULL) { free(s_profiling_recbuf); s_profiling_recbuf = NULL; } if (s_ll_recbuf != NULL) { free(s_ll_recbuf); s_ll_recbuf = NULL; } } void os_perfthr_quit_wait(void) { /* Not supported in Linux. */ } int os_perf_profiling_partpause(count_id_t count_id) { perf_task_t task; task_partpause_t *t; memset(&task, 0, sizeof (perf_task_t)); t = (task_partpause_t *)&task; t->task_id = PERF_PROFILING_PARTPAUSE_ID; t->count_id = count_id; perf_task_set(&task); return (perf_status_wait(PERF_STATUS_PROFILING_PART_STARTED)); } int os_perf_profiling_restore(count_id_t count_id) { perf_task_t task; task_restore_t *t; memset(&task, 0, sizeof (perf_task_t)); t = (task_restore_t *)&task; t->task_id = PERF_PROFILING_RESTORE_ID; t->count_id = count_id; perf_task_set(&task); return (perf_status_wait(PERF_STATUS_PROFILING_STARTED)); } int os_perf_callchain_start(pid_t pid, int lwpid) { /* Not supported in Linux. */ return (0); } int os_perf_callchain_smpl(void) { /* Not supported in Linux. */ return (0); } int os_perf_ll_smpl(perf_ctl_t *ctl, pid_t pid, int lwpid) { perf_task_t task; task_ll_t *t; perf_smpl_wait(); memset(&task, 0, sizeof (perf_task_t)); t = (task_ll_t *)&task; t->task_id = PERF_LL_SMPL_ID; t->pid = pid; t->lwpid = lwpid; perf_task_set(&task); return (0); } void os_perf_countchain_reset(perf_countchain_t *count_chain) { perf_chainrecgrp_t *grp; int i; for (i = 0; i < COUNT_NUM; i++) { grp = &count_chain->chaingrps[i]; if (grp->rec_arr != NULL) { free(grp->rec_arr); } } memset(count_chain, 0, sizeof (perf_countchain_t)); } void os_allstop(void) { if (perf_profiling_started()) { profiling_stop(); } if (perf_ll_started()) { ll_stop(); } } int os_perf_allstop(void) { perf_task_t task; task_allstop_t *t; memset(&task, 0, sizeof (perf_task_t)); t = (task_allstop_t *)&task; t->task_id = PERF_STOP_ID; perf_task_set(&task); return (perf_status_wait(PERF_STATUS_IDLE)); } void * os_perf_priv_alloc(boolean_t *supported) { /* Not supported in Linux. */ *supported = B_FALSE; return (NULL); } void os_perf_priv_free(void *priv) { /* Not supported in Linux. */ } int os_perf_chain_nentries(perf_chainrecgrp_t *grp, int *nchains) { /* Not supported in Linux. */ return (0); } perf_chainrec_t * os_perf_chainrec_get(perf_chainrecgrp_t *grp, int rec_idx) { /* Not supported in Linux. */ return (NULL); } char * os_perf_chain_entryname(void *buf, int depth_idx) { /* Not supported in Linux. */ return (NULL); } void os_perf_cpuarr_init(perf_cpu_t *cpu_arr, int num, boolean_t hotadd) { int i; for (i = 0; i < num; i++) { cpu_arr[i].cpuid = INVALID_CPUID; cpu_arr[i].hotadd = hotadd; cpu_init(&cpu_arr[i]); } } void os_perf_cpuarr_fini(perf_cpu_t *cpu_arr, int num, boolean_t hotremove) { int i; for (i = 0; i < num; i++) { if (cpu_arr[i].cpuid != INVALID_CPUID) { cpu_arr[i].hotremove = hotremove; } } } static perf_cpu_t * cpu_find(perf_cpu_t *cpu_arr, int cpu_num, int cpuid) { int i; for (i = 0; i < cpu_num; i++) { if (cpu_arr[i].cpuid == cpuid) { return (&cpu_arr[i]); } } return (NULL); } static int free_idx_get(perf_cpu_t *cpu_arr, int cpu_num, int prefer_idx) { int i; if ((prefer_idx >= 0) && (prefer_idx < cpu_num)) { if (cpu_arr[prefer_idx].cpuid == INVALID_CPUID) { return (prefer_idx); } } for (i = 0; i < cpu_num; i++) { if (cpu_arr[i].cpuid == INVALID_CPUID) { return (i); } } return (-1); } int os_perf_cpuarr_refresh(perf_cpu_t *cpu_arr, int cpu_num, int *cpuid_arr, int id_num, boolean_t init) { int i, j, k; perf_cpu_t *cpu; for (i = 0; i < cpu_num; i++) { cpu_arr[i].hit = B_FALSE; } for (i = 0; i < id_num; i++) { if ((cpu = cpu_find(cpu_arr, cpu_num, cpuid_arr[i])) == NULL) { /* * New CPU found. */ if ((j = free_idx_get(cpu_arr, cpu_num, i)) == -1) { return (-1); } cpu_arr[j].cpuid = cpuid_arr[i]; cpu_arr[j].map_base = MAP_FAILED; for (k = 0; k < COUNT_NUM; k++) { cpu_arr[j].fds[k] = INVALID_FD; } cpu_arr[j].hit = B_TRUE; cpu_arr[j].hotadd = !init; if (cpu_arr[j].hotadd) { debug_print(NULL, 2, "cpu%d is hot-added.\n", cpu_arr[i].cpuid); } } else { cpu->hit = B_TRUE; } } for (i = 0; i < cpu_num; i++) { if ((!cpu_arr[i].hit) && (cpu_arr[i].cpuid != INVALID_CPUID)) { /* * This CPU is invalid now. */ cpu_arr[i].hotremove = B_TRUE; debug_print(NULL, 2, "cpu%d is hot-removed.\n", cpu_arr[i].cpuid); } } return (0); } numatop/common/os/plat.c0000644000175000017500000001060012633415311015116 0ustar jinyaojinyao/* * 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 a general layer of functions. */ #include #include #include #include #include #include #include #include "../include/types.h" #include "../include/util.h" #include "../include/os/plat.h" #include "../../intel/include/nhm.h" #include "../../intel/include/wsm.h" #include "../../intel/include/snb.h" #include "../../intel/include/bdw.h" static pfn_plat_profiling_config_t s_plat_profiling_config[CPU_TYPE_NUM] = { NULL, wsmex_profiling_config, snbep_profiling_config, nhmex_profiling_config, nhmep_profiling_config, wsmep_profiling_config, snbep_profiling_config, snbep_profiling_config, bdw_profiling_config }; static pfn_plat_ll_config_t s_plat_ll_config[CPU_TYPE_NUM] = { NULL, wsmex_ll_config, snbep_ll_config, nhmex_ll_config, nhmep_ll_config, wsmep_ll_config, snbep_ll_config, snbep_ll_config, bdw_ll_config }; static pfn_plat_offcore_num_t s_plat_offcore_num[CPU_TYPE_NUM] = { NULL, wsm_offcore_num, snb_offcore_num, nhm_offcore_num, nhm_offcore_num, wsm_offcore_num, snb_offcore_num, snb_offcore_num, bdw_offcore_num }; static cpu_type_t s_cpu_type; /* * NumaTOP needs some special performance counters, * It can only run on WSM-EX/SNB-EP platforms now. */ int plat_detect(void) { if ((s_cpu_type = cpu_type_get()) == CPU_UNSUP) { return (-1); } switch (s_cpu_type) { case CPU_WSM_EX: /* fall through */ case CPU_SNB_EP: /* fall through */ case CPU_NHM_EX: /* fall through */ case CPU_NHM_EP: /* fall through */ case CPU_WSM_EP: /* fall through */ case CPU_IVB_EX: /* fall through */ case CPU_HSX: /* fall through */ case CPU_BDX: return (0); default: break; } return (-1); } /* * Platform-independent function to get the event configuration for profiling. */ void plat_profiling_config(count_id_t count_id, plat_event_config_t *cfg) { pfn_plat_profiling_config_t pfn = s_plat_profiling_config[s_cpu_type]; if (pfn != NULL) { pfn(count_id, cfg); } } /* * Platform-independent function to get the event configuration for LL. */ void plat_ll_config(plat_event_config_t *cfg) { pfn_plat_ll_config_t pfn = s_plat_ll_config[s_cpu_type]; if (pfn != NULL) { pfn(cfg); } } void plat_config_get(count_id_t count_id, plat_event_config_t *cfg, plat_event_config_t *cfg_arr) { cfg->type = cfg_arr[count_id].type; cfg->config = cfg_arr[count_id].config; cfg->other_attr = cfg_arr[count_id].other_attr; cfg->extra_value = cfg_arr[count_id].extra_value; strncpy(cfg->desc, cfg_arr[count_id].desc, PLAT_EVENT_DESC_SIZE); cfg->desc[PLAT_EVENT_DESC_SIZE - 1] = 0; } /* * Platform-independent function to return the number of offcore counters. */ int plat_offcore_num(void) { pfn_plat_offcore_num_t pfn = s_plat_offcore_num[s_cpu_type]; if (pfn != NULL) { return (pfn()); } return (0); } numatop/common/os/os_util.c0000664000175000017500000002750312633407552015660 0ustar jinyaojinyao/* * 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. */ #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include "../include/types.h" #include "../include/util.h" #include "../include/os/os_util.h" uint64_t g_clkofsec; double g_nsofclk; boolean_t os_authorized(void) { return (B_TRUE); } int os_numatop_lock(boolean_t *locked) { /* Not supported on Linux */ return (0); } void os_numatop_unlock(void) { /* Not supported on Linux */ } int os_procfs_psinfo_get(pid_t pid, void *info) { /* Not supported on Linux */ return (0); } /* * Retrieve the process's executable name from '/proc' */ int os_procfs_pname_get(pid_t pid, char *buf, int size) { char pname[PATH_MAX]; int procfd; /* file descriptor for /proc/nnnnn/comm */ int len; snprintf(pname, sizeof (pname), "/proc/%d/comm", pid); if ((procfd = open(pname, O_RDONLY)) < 0) { return (-1); } if ((len = read(procfd, buf, size)) < 0) { (void) close(procfd); return (-1); } buf[len - 1] = 0; (void) close(procfd); return (0); } /* * Retrieve the lwpid in process from '/proc'. */ int os_procfs_lwp_enum(pid_t pid, int **lwps, int *num) { char path[PATH_MAX]; (void) snprintf(path, sizeof (path), "/proc/%d/task", pid); return (procfs_enum_id(path, lwps, num)); } /* * Check if the specified pid/lwpid can be found in '/proc'. */ boolean_t os_procfs_lwp_valid(pid_t pid, int lwpid) { /* Not supported on Linux */ return (B_TRUE); } /* * Get the TSC cycles. */ #ifdef __x86_64__ static uint64_t rdtsc() { uint64_t var; uint32_t hi, lo; __asm volatile ("rdtsc" : "=a" (lo), "=d" (hi)); /* LINTED E_VAR_USED_BEFORE_SET */ var = ((uint64_t)hi << 32) | lo; return (var); } #else static uint64_t rdtsc() { uint64_t var; __asm volatile ("rdtsc" : "=A" (var)); return (var); } #endif /* * Bind current thread to a cpu or unbind current thread * from a cpu. */ static int processor_bind(int cpu) { cpu_set_t cs; CPU_ZERO (&cs); CPU_SET (cpu, &cs); if (sched_setaffinity(0, sizeof (cs), &cs) < 0) { debug_print(NULL, 2, "Fail to bind to CPU%d\n", cpu); return (-1); } return (0); } static int processor_unbind(void) { cpu_set_t cs; int i; CPU_ZERO (&cs); for (i = 0; i < g_ncpus; i++) { CPU_SET (i, &cs); } if (sched_setaffinity(0, sizeof (cs), &cs) < 0) { debug_print(NULL, 2, "Fail to unbind from CPU\n"); return (-1); } return (0); } /* * Check the cpu name in proc info. Intel CPUs always have @ x.y * Ghz and that is the TSC frequency. */ static int calibrate_cpuinfo(double *nsofclk, uint64_t *clkofsec) { FILE *f; char *line = NULL, unit[11]; size_t len = 0; double freq = 0.0; if ((f = fopen(CPUINFO_PATH, "r")) == NULL) { return (-1); } while (getline(&line, &len, f) > 0) { if (strncmp(line, "model name", sizeof ("model name") - 1) != 0) { continue; } if (sscanf(line + strcspn(line, "@") + 1, "%lf%10s", &freq, unit) == 2) { if (strcasecmp(unit, "GHz") == 0) { freq *= GHZ; } else if (strcasecmp(unit, "Mhz") == 0) { freq *= MHZ; } break; } } free(line); fclose(f); if (freq == 0.0) { return (-1); } *clkofsec = freq; *nsofclk = (double)NS_SEC / *clkofsec; debug_print(NULL, 2, "calibrate_cpuinfo: nsofclk = %.4f, " "clkofsec = %lu\n", *nsofclk, *clkofsec); return (0); } /* * On all recent Intel CPUs, the TSC frequency is always * the highest p-state. So get that frequency from sysfs. * e.g. 2262000 */ static int calibrate_cpufreq(double *nsofclk, uint64_t *clkofsec) { int fd, i; char buf[32]; uint64_t freq; if ((fd = open(CPU0_CPUFREQ_PATH, O_RDONLY)) < 0) { return (-1); } if ((i = read(fd, buf, sizeof (buf) - 1)) <= 0) { close(fd); return (-1); } close(fd); buf[i] = 0; if ((freq = atoll(buf)) == 0) { return (-1); } *clkofsec = freq * KHZ; *nsofclk = (double)NS_SEC / *clkofsec; debug_print(NULL, 2, "calibrate_cpufreq: nsofclk = %.4f, " "clkofsec = %lu\n", *nsofclk, *clkofsec); return (0); } /* * Measure how many TSC cycles in a second and how many * nanoseconds in a TSC cycle. */ static void calibrate_by_tsc(double *nsofclk, uint64_t *clkofsec) { uint64_t start_ms, end_ms, diff_ms; uint64_t start_tsc, end_tsc; int i; for (i = 0; i < g_ncpus; i++) { /* * Bind current thread to cpuN to ensure the * thread can not be migrated to another cpu * while the rdtsc runs. */ if (processor_bind(i) == 0) { break; } } if (i == g_ncpus) { return; } /* * Make sure the start_ms is at the beginning of * one millisecond. */ end_ms = current_ms(); while ((start_ms = current_ms()) == end_ms) {} start_tsc = rdtsc(); while ((end_ms = current_ms()) < (start_ms + 100)) {} end_tsc = rdtsc(); diff_ms = end_ms - start_ms; *nsofclk = (double)(diff_ms * NS_MS) / (double)(end_tsc - start_tsc); *clkofsec = (uint64_t)((double)NS_SEC / *nsofclk); /* * Unbind current thread from cpu once the measurement completed. */ processor_unbind(); debug_print(NULL, 2, "calibrate_by_tsc: nsofclk = %.4f, " "clkofsec = %lu\n", *nsofclk, *clkofsec); } void os_calibrate(void) { if (calibrate_cpuinfo(&g_nsofclk, &g_clkofsec) == 0) { return; } if (calibrate_cpufreq(&g_nsofclk, &g_clkofsec) == 0) { return; } calibrate_by_tsc(&g_nsofclk, &g_clkofsec); } static boolean_t int_get(char *str, int *digit) { char *end; long val; /* Distinguish success/failure after strtol */ errno = 0; val = strtol(str, &end, 10); if (((errno == ERANGE) && ((val == LONG_MAX) || (val == LONG_MIN))) || ((errno != 0) && (val == 0))) { return (B_FALSE); } if (end == str) { return (B_FALSE); } *digit = val; return (B_TRUE); } /* * The function is only called for processing small digits. * For example, if the string is "0-9", extract the digit 0 and 9. */ static boolean_t hyphen_int_extract(char *str, int *start, int *end) { char tmp[DIGIT_LEN_MAX]; if (strlen(str) >= DIGIT_LEN_MAX) { return (B_FALSE); } if (sscanf(str, "%511[^-]", tmp) <= 0) { return (B_FALSE); } if (!int_get(tmp, start)) { return (B_FALSE); } if (sscanf(str, "%*[^-]-%511s", tmp) <= 0) { return (B_FALSE); } if (!int_get(tmp, end)) { return (B_FALSE); } return (B_TRUE); } static boolean_t arrary_add(int *arr, int arr_size, int index, int value, int num) { int i; if ((index >= arr_size) || ((index + num) > arr_size)) { return (B_FALSE); } for (i = 0; i < num; i++) { arr[index + i] = value + i; } return (B_TRUE); } /* * Extract the digits from string. For example: * "1-2,5-7" return 1 2 5 6 7 in "arr". */ static boolean_t str_int_extract(char *str, int *arr, int arr_size, int *num) { char *p, *cur, *scopy; int start, end, total = 0; int len = strlen(str); boolean_t ret = B_FALSE; if ((scopy = malloc(len + 1)) == NULL) { return (B_FALSE); } strncpy(scopy, str, len); scopy[len] = 0; cur = scopy; while (cur < (scopy + len)) { if ((p = strchr(cur, ',')) != NULL) { *p = 0; } if (strchr(cur, '-') != NULL) { if (hyphen_int_extract(cur, &start, &end)) { if (arrary_add(arr, arr_size, total, start, end - start + 1)) { total += end - start + 1; } else { goto L_EXIT; } } } else { if (int_get(cur, &start)) { if (arrary_add(arr, arr_size, total, start, 1)) { total++; } else { goto L_EXIT; } } } if (p != NULL) { cur = p + 1; } else { break; } } *num = total; ret = B_TRUE; L_EXIT: free(scopy); return (ret); } static boolean_t file_int_extract(char *path, int *arr, int arr_size, int *num) { FILE *fp; char buf[LINE_SIZE]; if ((fp = fopen(path, "r")) == NULL) { return (B_FALSE); } if (fgets(buf, LINE_SIZE, fp) == NULL) { fclose(fp); return (B_FALSE); } fclose(fp); return (str_int_extract(buf, arr, arr_size, num)); } boolean_t os_sysfs_node_enum(int *node_arr, int arr_size, int *num) { return (file_int_extract(NODE_NONLINE_PATH, node_arr, arr_size, num)); } boolean_t os_sysfs_cpu_enum(int nid, int *cpu_arr, int arr_size, int *num) { char path[PATH_MAX]; snprintf(path, PATH_MAX, "%s/node%d/cpulist", NODE_INFO_ROOT, nid); return (file_int_extract(path, cpu_arr, arr_size, num)); } int os_sysfs_online_ncpus(void) { int cpu_arr[NCPUS_MAX], num; char path[PATH_MAX]; if (sysconf(_SC_NPROCESSORS_CONF) > NCPUS_MAX) { return (-1); } snprintf(path, PATH_MAX, "/sys/devices/system/cpu/online"); if (!file_int_extract(path, cpu_arr, NCPUS_MAX, &num)) { return (-1); } return (num); } static boolean_t memsize_parse(char *str, uint64_t *size) { char *p; char tmp[DIGIT_LEN_MAX]; if ((p = strchr(str, ':')) == NULL) { return (B_FALSE); } ++p; if (sscanf(p, "%*[^0-9]%511[0-9]", tmp) <= 0) { return (B_FALSE); } *size = strtoll(tmp, NULL, 10) * KB_BYTES; return (B_TRUE); } boolean_t os_sysfs_meminfo(int nid, node_meminfo_t *info) { FILE *fp; char path[PATH_MAX]; char *line = NULL; size_t len = 0; boolean_t ret = B_FALSE; int num = sizeof (node_meminfo_t) / sizeof (uint64_t), i = 0; memset(info, 0, sizeof (node_meminfo_t)); snprintf(path, PATH_MAX, "%s/node%d/meminfo", NODE_INFO_ROOT, nid); if ((fp = fopen(path, "r")) == NULL) { return (B_FALSE); } while ((getline(&line, &len, fp) > 0) && (i < num)) { if (strstr(line, "MemTotal:") != NULL) { if (!memsize_parse(line, &info->mem_total)) { goto L_EXIT; } i++; continue; } if (strstr(line, "MemFree:") != NULL) { if (!memsize_parse(line, &info->mem_free)) { goto L_EXIT; } i++; continue; } if (strstr(line, "Active:") != NULL) { if (!memsize_parse(line, &info->active)) { goto L_EXIT; } i++; continue; } if (strstr(line, "Inactive:") != NULL) { if (!memsize_parse(line, &info->inactive)) { goto L_EXIT; } i++; continue; } if (strstr(line, "Dirty:") != NULL) { if (!memsize_parse(line, &info->dirty)) { goto L_EXIT; } i++; continue; } if (strstr(line, "Writeback:") != NULL) { if (!memsize_parse(line, &info->writeback)) { goto L_EXIT; } i++; continue; } if (strstr(line, "Mapped:") != NULL) { if (!memsize_parse(line, &info->mapped)) { goto L_EXIT; } i++; continue; } } ret = B_TRUE; L_EXIT: if (line != NULL) { free(line); } fclose(fp); return (ret); } numatop/common/os/map.c0000664000175000017500000002173012633407552014753 0ustar jinyaojinyao/* * 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 the routines to process address mapping in running process. */ #include #include #include #include #include #include #include #include "../include/util.h" #include "../include/proc.h" #include "../include/os/os_util.h" #include "../include/os/map.h" int map_init(void) { pagesize_init(); return (0); } void map_fini(void) { /* Not supported in Linux. */ } static unsigned int attr_bitmap(char *attr_str) { unsigned int bitmap = 0; if (attr_str[0] == 'r') { MAP_R_SET(bitmap); } if (attr_str[1] == 'w') { MAP_W_SET(bitmap); } if (attr_str[2] == 'x') { MAP_X_SET(bitmap); } if (attr_str[3] == 'p') { MAP_P_SET(bitmap); } else if (attr_str[3] == 's') { MAP_S_SET(bitmap); } return (bitmap); } static int map_entry_add(map_proc_t *map, uint64_t start_addr, uint64_t end_addr, unsigned int attr, char *path) { map_entry_t *entry; if (array_alloc((void **)(&map->arr), &map->nentry_cur, &map->nentry_max, sizeof (map_entry_t), MAP_ENTRY_NUM) != 0) { return (-1); } entry = &(map->arr[map->nentry_cur]); entry->start_addr = start_addr; entry->end_addr = end_addr; entry->attr = attr; entry->need_resolve = B_TRUE; memset(&entry->numa_map, 0, sizeof (numa_map_t)); if (strlen(path) > 0) { strncpy(entry->desc, path, PATH_MAX); entry->desc[PATH_MAX - 1] = 0; } else { entry->desc[0] = 0; } map->nentry_cur++; return (0); } static void numa_map_fini(map_entry_t *entry) { numa_map_t *numa; numa = &entry->numa_map; if (numa->arr != NULL) { free(numa->arr); } memset(numa, 0, sizeof (numa_map_t)); } static void map_free(map_proc_t *map) { int i; if (map->arr == NULL) { return; } for (i = 0; i < map->nentry_cur; i++) { numa_map_fini(&map->arr[i]); } free(map->arr); memset(map, 0, sizeof (map_proc_t)); } static int map_read(pid_t pid, map_proc_t *map) { char path[PATH_MAX]; char line[MAPFILE_LINE_SIZE]; char addr_str[128], attr_str[128], off_str[128]; char fd_str[128], inode_str[128], path_str[PATH_MAX]; char s1[64], s2[64]; uint64_t start_addr, end_addr; unsigned int attr; int nargs, nadded = 0, ret = -1; FILE *fp; memset(map, 0, sizeof (map_proc_t)); snprintf(path, sizeof (path), "/proc/%d/maps", pid); if ((fp = fopen(path, "r")) == NULL) { return (-1); } while (1) { if (fgets(line, sizeof (line), fp) == NULL) { break; } /* * e.g. 00400000-00405000 r-xp 00000000 fd:00 678793 /usr/bin/vmstat */ if ((nargs = sscanf(line, "%127[^ ] %127[^ ] %127[^ ] %127[^ ] %127[^ ] %4095[^\n]", addr_str, attr_str, off_str, fd_str, inode_str, path_str)) < 0) { goto L_EXIT; } /* * split to start_addr and end_addr. * e.g. 00400000-00405000 -> start_addr = 00400000, end_addr = 00405000. */ if (sscanf(addr_str, "%63[^-]", s1) <= 0) { goto L_EXIT; } if (sscanf(addr_str, "%*[^-]-%63s", s2) <= 0) { goto L_EXIT; } start_addr = strtoull(s1, NULL, 16); end_addr = strtoull(s2, NULL, 16); /* * Convert to the attribute bitmap */ attr = attr_bitmap(attr_str); /* * Path could be null, need to check here. */ if (nargs != 6) { path_str[0] = 0; } if (map_entry_add(map, start_addr, end_addr, attr, path_str) != 0) { goto L_EXIT; } nadded++; } if (nadded > 0) { map->loaded = B_TRUE; ret = 0; } L_EXIT: fclose(fp); if ((ret != 0) && (nadded > 0)) { map_free(map); } return (ret); } int map_proc_load(track_proc_t *proc) { map_proc_t *map = &proc->map; map_proc_t new_map; map_entry_t *old_entry; int i; if (!map->loaded) { if (map_read(proc->pid, map) != 0) { return (-1); } return (0); } if (map_read(proc->pid, &new_map) != 0) { return (-1); } for (i = 0; i < new_map.nentry_cur; i++) { if ((old_entry = map_entry_find(proc, new_map.arr[i].start_addr, new_map.arr[i].end_addr - new_map.arr[i].start_addr)) == NULL) { new_map.arr[i].need_resolve = B_TRUE; } else { new_map.arr[i].need_resolve = old_entry->need_resolve; } } map_free(&proc->map); memcpy(&proc->map, &new_map, sizeof (map_proc_t)); return (0); } int map_proc_fini(track_proc_t *proc) { map_free(&proc->map); return (0); } /* * The callback function used in bsearch() to compare the buffer address. */ static int entryaddr_cmp(const void *p1, const void *p2) { uint64_t addr = *(uint64_t *)p1; map_entry_t *entry = (map_entry_t *)p2; if (addr < entry->start_addr) { return (-1); } if (addr >= entry->end_addr) { return (1); } return (0); } map_entry_t * map_entry_find(track_proc_t *proc, uint64_t addr, uint64_t size) { map_entry_t *entry; entry = bsearch(&addr, proc->map.arr, proc->map.nentry_cur, sizeof (map_entry_t), entryaddr_cmp); if (entry != NULL) { if ((entry->start_addr == addr) && (entry->end_addr == addr + size)) { return (entry); } } return (NULL); } static numa_entry_t * numa_entry_add(numa_map_t *numa_map, uint64_t addr, int nid) { numa_entry_t *entry; if (array_alloc((void **)(&numa_map->arr), &numa_map->nentry_cur, &numa_map->nentry_max, sizeof (numa_entry_t), MAP_ENTRY_NUM) != 0) { return (NULL); } entry = &(numa_map->arr[numa_map->nentry_cur]); entry->start_addr = addr; entry->end_addr = addr + g_pagesize; entry->nid = nid; numa_map->nentry_cur++; return (entry); } static numa_entry_t * numa_map_update(numa_map_t *numa_map, void **addr_arr, int *node_arr, int addr_num, numa_entry_t *last_entry) { numa_entry_t *entry; int i = 0, j; if ((entry = last_entry) == NULL) { if ((entry = numa_entry_add(numa_map, (uint64_t)(addr_arr[i]), node_arr[i])) == NULL) { return (NULL); } i++; } for (j = i; j < addr_num; j++) { if ((entry->nid == node_arr[j]) && (entry->end_addr == (uint64_t)(addr_arr[j]))) { entry->end_addr += g_pagesize; } else { if ((entry = numa_entry_add(numa_map, (uint64_t)(addr_arr[j]), node_arr[j])) == NULL) { return (NULL); } } } return (entry); } int map_map2numa(track_proc_t *proc, map_entry_t *map_entry) { void *addr_arr[NUMA_MOVE_NPAGES]; unsigned int i, npages_total, npages_tomove, npages_moved = 0; int node_arr[NUMA_MOVE_NPAGES]; numa_entry_t *last_entry = NULL; numa_map_fini(map_entry); npages_total = (map_entry->end_addr - map_entry->start_addr) / g_pagesize; while (npages_moved < npages_total) { npages_tomove = MIN(NUMA_MOVE_NPAGES, npages_total - npages_moved); for (i = 0; i < npages_tomove; i++) { addr_arr[i] = (void *)(map_entry->start_addr + (i + npages_moved) * g_pagesize); } memset(node_arr, 0, sizeof (node_arr)); if (numa_move_pages(proc->pid, npages_tomove, addr_arr, NULL, node_arr, 0) != 0) { return (-1); } if ((last_entry = numa_map_update(&map_entry->numa_map, addr_arr, node_arr, npages_tomove, last_entry)) == NULL) { return (-1); } npages_moved += npages_tomove; } return (0); } int map_addr2nodedst(pid_t pid, void **addr_arr, int *lat_arr, int addr_num, map_nodedst_t *nodedst_arr, int nnodes, int *naccess_total) { int *status_arr, i, nid; if ((status_arr = zalloc(sizeof (int) * addr_num)) == NULL) { return (-1); } if (numa_move_pages(pid, addr_num, addr_arr, NULL, status_arr, 0) != 0) { free(status_arr); return (-1); } *naccess_total = 0; for (i = 0; i < addr_num; i++) { nid = status_arr[i]; if ((nid >= 0) && (nid < nnodes)) { nodedst_arr[nid].naccess++; nodedst_arr[nid].total_lat += lat_arr[i]; *naccess_total += 1; } } free(status_arr); return (0); } numatop/common/os/os_page.c0000664000175000017500000000510512633407552015611 0ustar jinyaojinyao/* * 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 #include #include #include #include #include #include #include "../include/types.h" #include "../include/cmd.h" #include "../include/page.h" #include "../include/perf.h" #include "../include/os/os_page.h" /* * Start sampling the performance data. */ boolean_t os_page_smpl_start(page_t *page) { cmd_t *cmd = PAGE_CMD(page); switch (CMD_ID(cmd)) { case CMD_HOME_ID: /* fall through */ case CMD_IR_NORMALIZE_ID: /* fall through */ case CMD_MONITOR_ID: /* fall through */ case CMD_LWP_ID: /* fall through */ case CMD_NODE_OVERVIEW_ID: /* fall through */ case CMD_NODE_DETAIL_ID: /* fall through */ case CMD_CALLCHAIN_ID: if (perf_profiling_smpl() == 0) { return (B_TRUE); } break; case CMD_LAT_ID: /* fall through */ case CMD_LLCALLCHAIN_ID: /* fall through */ case CMD_LATNODE_ID: /* fall through */ case CMD_ACCDST_ID: if (perf_ll_smpl(0, 0) == 0) { return (B_TRUE); } break; default: break; } return (B_FALSE); } numatop/common/proc.c0000664000175000017500000004660512633407552014530 0ustar jinyaojinyao/* * 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 handle the 'tracked process'. */ #include #include #include #include #include #include #include #include #include #include #include "include/types.h" #include "include/lwp.h" #include "include/proc.h" #include "include/disp.h" #include "include/util.h" #include "include/perf.h" #include "include/os/node.h" #include "include/os/os_util.h" static proc_group_t s_proc_group; /* * Initialization for the process group. */ int proc_group_init(void) { (void) memset(&s_proc_group, 0, sizeof (s_proc_group)); if (pthread_mutex_init(&s_proc_group.mutex, NULL) != 0) { return (-1); } if (pthread_cond_init(&s_proc_group.cond, NULL) != 0) { (void) pthread_mutex_destroy(&s_proc_group.mutex); return (-1); } s_proc_group.inited = B_TRUE; return (0); } /* ARGSUSED */ static int lwp_free_walk(track_lwp_t *lwp, void *arg, boolean_t *end) { *end = B_FALSE; (void) lwp_free(lwp); return (0); } /* * Free resources of 'track_proc_t' if 'ref_count' is 0. */ static void proc_free(track_proc_t *proc) { proc_lwplist_t *list = &proc->lwp_list; (void) pthread_mutex_lock(&proc->mutex); if (proc->ref_count > 0) { proc->removing = B_TRUE; (void) pthread_mutex_unlock(&proc->mutex); return; } proc_lwp_traverse(proc, lwp_free_walk, NULL); s_proc_group.nprocs--; s_proc_group.nlwps -= list->nlwps; if (list->id_arr != NULL) { free(list->id_arr); } if (list->sort_arr != NULL) { free(list->sort_arr); } if (proc->countval_arr != NULL) { free(proc->countval_arr); } (void) map_proc_fini(proc); sym_free(&proc->sym); perf_countchain_reset(&proc->count_chain); perf_llrecgrp_reset(&proc->llrec_grp); (void) pthread_mutex_unlock(&proc->mutex); (void) pthread_mutex_destroy(&proc->mutex); free(proc); } /* * Walk through all processes and call 'func()' for each processes. */ static void proc_traverse(int (*func)(track_proc_t *, void *, boolean_t *), void *arg) { track_proc_t *proc, *hash_next; boolean_t end; int i, j = 0; /* * The mutex of s_proc_group has been taken outside. */ for (i = 0; i < PROC_HASHTBL_SIZE; i++) { proc = s_proc_group.hashtbl[i]; while (proc != NULL) { j++; hash_next = proc->hash_next; func(proc, arg, &end); if (end) { return; } proc = hash_next; } if (j == s_proc_group.nprocs) { return; } } } /* ARGSUSED */ static int proc_free_walk(track_proc_t *proc, void *arg, boolean_t *end) { *end = B_FALSE; proc_free(proc); return (0); } /* * Free all the resources of process group. */ void proc_group_fini(void) { if (!s_proc_group.inited) { return; } (void) pthread_mutex_lock(&s_proc_group.mutex); proc_traverse(proc_free_walk, NULL); if (s_proc_group.sort_arr != NULL) { free(s_proc_group.sort_arr); } (void) pthread_mutex_unlock(&s_proc_group.mutex); (void) pthread_mutex_destroy(&s_proc_group.mutex); (void) pthread_cond_destroy(&s_proc_group.cond); } /* * Look for a process by specified pid. */ static track_proc_t * proc_find_nolock(pid_t pid) { track_proc_t *proc; int hashidx; /* * To speed up, check the "latest access" process first. */ if ((s_proc_group.latest != NULL) && ((s_proc_group.latest)->pid == pid)) { proc = s_proc_group.latest; goto L_EXIT; } /* * Scan the process hash table. */ hashidx = PROC_HASHTBL_INDEX(pid); proc = s_proc_group.hashtbl[hashidx]; while (proc != NULL) { if (proc->pid == pid) { break; } proc = proc->hash_next; } L_EXIT: if (proc != NULL) { if (proc_refcount_inc(proc) != 0) { /* * The proc is tagged as removing. */ if (s_proc_group.latest == proc) { s_proc_group.latest = NULL; } proc = NULL; } } if (proc != NULL) { s_proc_group.latest = proc; } return (proc); } /* * Look for a process by pid with lock protection. */ track_proc_t * proc_find(pid_t pid) { track_proc_t *proc; (void) pthread_mutex_lock(&s_proc_group.mutex); proc = proc_find_nolock(pid); (void) pthread_mutex_unlock(&s_proc_group.mutex); return (proc); } /* * Allocation and initialization for a new 'track_proc_t' structure. */ static track_proc_t * proc_alloc(void) { int cpuid_max; track_proc_t *proc; count_value_t *countval_arr; if ((cpuid_max = node_cpuid_max()) <= 0) { return (NULL); } if ((countval_arr = zalloc(cpuid_max * sizeof (count_value_t))) == NULL) { return (NULL); } if ((proc = zalloc(sizeof (track_proc_t))) == NULL) { free(countval_arr); return (NULL); } if (pthread_mutex_init(&proc->mutex, NULL) != 0) { free(countval_arr); free(proc); return (NULL); } proc->pid = -1; proc->countval_arr = countval_arr; proc->cpuid_max = cpuid_max; proc->inited = B_TRUE; return (proc); } static int lwp_id_cmp(const void *a, const void *b) { track_lwp_t *lwp1 = (track_lwp_t *)a; track_lwp_t *lwp2 = *((track_lwp_t **)b); if (lwp1->id > lwp2->id) { return (1); } if (lwp1->id < lwp2->id) { return (-1); } return (0); } /* * Look for a thread in 'lwp_list' of proc. */ track_lwp_t * proc_lwp_find(track_proc_t *proc, id_t lwpid) { track_lwp_t *lwp = NULL, **p, lwp_key; proc_lwplist_t *list = &proc->lwp_list; lwp_key.id = lwpid; (void) pthread_mutex_lock(&proc->mutex); if ((p = bsearch(&lwp_key, (void *)(list->id_arr), list->nlwps, sizeof (track_lwp_t *), lwp_id_cmp)) != NULL) { lwp = *p; if (lwp_refcount_inc(lwp) != 0) { /* * The lwp is being removed by other threads or * the thread is quitting. */ lwp = NULL; } } (void) pthread_mutex_unlock(&proc->mutex); return (lwp); } static int lwp_key_cmp(const void *a, const void *b) { track_lwp_t *lwp1 = *((track_lwp_t **)a); track_lwp_t *lwp2 = *((track_lwp_t **)b); if (lwp1->key > lwp2->key) { return (-1); } if (lwp1->key < lwp2->key) { return (1); } return (0); } static void proc_lwp_sortkey(track_proc_t *proc) { proc_lwplist_t *list = &proc->lwp_list; track_lwp_t **sort_arr; if (list->sort_arr != NULL) { free(list->sort_arr); list->sort_arr = NULL; } if ((sort_arr = zalloc(sizeof (track_lwp_t *) * list->nlwps)) == NULL) { return; } (void) memcpy(sort_arr, list->id_arr, sizeof (track_lwp_t *) * list->nlwps); qsort(sort_arr, list->nlwps, sizeof (track_lwp_t *), lwp_key_cmp); list->sort_arr = sort_arr; list->sort_idx = 0; } /* * Sort the threads in a specified process. */ void proc_lwp_resort(track_proc_t *proc, sort_key_t sort) { /* * The lock "proc->mutex" takes outside. */ proc_lwp_traverse(proc, lwp_key_compute, &sort); proc_lwp_sortkey(proc); } /* * Count the total number of processes and threads. */ void proc_lwp_count(int *nprocs, int *nlwps) { if (nprocs != NULL) { *nprocs = s_proc_group.nprocs; } if (nlwps != NULL) { *nlwps = s_proc_group.nlwps; } } void proc_group_lock(void) { (void) pthread_mutex_lock(&s_proc_group.mutex); } void proc_group_unlock(void) { (void) pthread_mutex_unlock(&s_proc_group.mutex); } static uint64_t count_value_get(track_proc_t *proc, count_id_t count_id) { return (node_countval_sum(proc->countval_arr, proc->cpuid_max, NODE_ALL, count_id)); } /* * Compute the value of key for process sorting. */ /* ARGSUSED */ static int proc_key_compute(track_proc_t *proc, void *arg, boolean_t *end) { sort_key_t sortkey = *((sort_key_t *)arg); uint64_t rma, lma, clk, ir; switch (sortkey) { case SORT_KEY_CPU: proc->key = count_value_get(proc, COUNT_CLK); break; case SORT_KEY_PID: proc->key = proc->pid; break; case SORT_KEY_RPI: rma = count_value_get(proc, COUNT_RMA); ir = count_value_get(proc, COUNT_IR); proc->key = (uint64_t)ratio(rma * 1000, ir); break; case SORT_KEY_LPI: lma = count_value_get(proc, COUNT_LMA); ir = count_value_get(proc, COUNT_IR); proc->key = (uint64_t)ratio(lma * 1000, ir); break; case SORT_KEY_CPI: clk = count_value_get(proc, COUNT_CLK); ir = count_value_get(proc, COUNT_IR); proc->key = (uint64_t)ratio(clk * 1000, ir); break; case SORT_KEY_RMA: proc->key = count_value_get(proc, COUNT_RMA); break; case SORT_KEY_LMA: proc->key = count_value_get(proc, COUNT_LMA); break; case SORT_KEY_RL: rma = count_value_get(proc, COUNT_RMA); lma = count_value_get(proc, COUNT_LMA); proc->key = (uint64_t)ratio(rma * 1000, lma); break; default: break; } *end = B_FALSE; return (0); } static int proc_key_cmp(const void *a, const void *b) { track_proc_t *proc1 = *((track_proc_t **)a); track_proc_t *proc2 = *((track_proc_t **)b); if (proc1->key > proc2->key) { return (-1); } if (proc1->key < proc2->key) { return (1); } return (0); } static int proc_pid_cmp(const void *a, const void *b) { track_proc_t *proc1 = *((track_proc_t **)a); track_proc_t *proc2 = *((track_proc_t **)b); if (proc1->pid > proc2->pid) { return (1); } if (proc1->pid < proc2->pid) { return (-1); } return (0); } static void proc_sortkey(void) { track_proc_t **sort_arr, *proc; int i, j = 0; if (s_proc_group.sort_arr != NULL) { free(s_proc_group.sort_arr); s_proc_group.sort_arr = NULL; } sort_arr = zalloc(sizeof (track_proc_t *) * s_proc_group.nprocs); if (sort_arr == NULL) { return; } for (i = 0; i < PROC_HASHTBL_SIZE; i++) { proc = s_proc_group.hashtbl[i]; while (proc != NULL) { sort_arr[j++] = proc; proc = proc->hash_next; } if (j == s_proc_group.nprocs) { break; } } qsort(sort_arr, s_proc_group.nprocs, sizeof (track_proc_t *), proc_pid_cmp); qsort(sort_arr, s_proc_group.nprocs, sizeof (track_proc_t *), proc_key_cmp); s_proc_group.sort_arr = sort_arr; s_proc_group.sort_idx = 0; } /* * Resort the process by the value of key. */ void proc_resort(sort_key_t sort) { /* * The lock of s_proc_group takes outside. */ proc_traverse(proc_key_compute, &sort); proc_sortkey(); } /* * Move the 'sort_idx' to next proc node and return current one. */ track_proc_t * proc_sort_next(void) { int idx = s_proc_group.sort_idx; if (s_proc_group.sort_arr == NULL) { return (NULL); } if (idx < s_proc_group.nprocs) { s_proc_group.sort_idx++; return (s_proc_group.sort_arr[idx]); } return (NULL); } int proc_nlwp(track_proc_t *proc) { return (proc->lwp_list.nlwps); } /* * Add a new proc in s_process_group->hashtbl. */ static int proc_group_add(track_proc_t *proc) { track_proc_t *head; int hashidx; /* * The lock of table has been taken outside. */ hashidx = PROC_HASHTBL_INDEX(proc->pid); if ((head = s_proc_group.hashtbl[hashidx]) != NULL) { head->hash_prev = proc; } proc->hash_next = head; proc->hash_prev = NULL; s_proc_group.hashtbl[hashidx] = proc; s_proc_group.nprocs++; return (0); } /* * Remove a specifiled proc from s_process_group->hashtbl. */ static void proc_group_remove(track_proc_t *proc) { track_proc_t *prev, *next; int hashidx; /* * The lock of table has been taken outside. */ hashidx = PROC_HASHTBL_INDEX(proc->pid); /* * Remove it from process hash-list. */ prev = proc->hash_prev; next = proc->hash_next; if (prev != NULL) { prev->hash_next = next; } else { s_proc_group.hashtbl[hashidx] = next; } if (next != NULL) { next->hash_prev = prev; } s_proc_group.nprocs--; if (s_proc_group.latest == proc) { s_proc_group.latest = NULL; } } /* * The process is not valid, remove it. */ static void proc_obsolete(pid_t pid) { track_proc_t *proc; if ((proc = proc_find(pid)) != NULL) { proc_refcount_dec(proc); (void) pthread_mutex_lock(&s_proc_group.mutex); proc_group_remove(proc); proc_free(proc); (void) pthread_mutex_unlock(&s_proc_group.mutex); } } static int pid_cmp(const void *a, const void *b) { pid_t *pid1 = (pid_t *)a; pid_t *pid2 = (pid_t *)b; if (*pid1 > *pid2) { return (1); } if (*pid1 < *pid2) { return (-1); } return (0); } static pid_t * pid_find(pid_t pid, pid_t *pid_arr, int num) { pid_t *p; p = bsearch(&pid, (void *)pid_arr, num, sizeof (pid_t), pid_cmp); return (p); } /* ARGSUSED */ static int proc_lwp_refresh(track_proc_t *proc, void *arg, boolean_t *end) { *end = B_FALSE; lwp_enum_update(proc); return (0); } /* ARGSUSED */ static int proc_nlwps_sum(track_proc_t *proc, void *arg, boolean_t *end) { *end = B_FALSE; s_proc_group.nlwps += proc_nlwp(proc); return (0); } /* * The array 'procs_new' contains the latest valid pid. Scan the hashtbl to * figure out the obsolete processes and remove them. For the new processes, * add them in hashtbl. */ static void proc_group_refresh(pid_t *procs_new, int nproc_new) { track_proc_t *proc, *hash_next; pid_t *p; int i, j; boolean_t *exist_arr; if ((exist_arr = zalloc(sizeof (boolean_t) * nproc_new)) == NULL) { return; } qsort(procs_new, nproc_new, sizeof (pid_t), pid_cmp); (void) pthread_mutex_lock(&s_proc_group.mutex); for (i = 0; i < PROC_HASHTBL_SIZE; i++) { proc = s_proc_group.hashtbl[i]; while (proc != NULL) { hash_next = proc->hash_next; if ((p = pid_find(proc->pid, procs_new, nproc_new)) == NULL) { proc_group_remove(proc); proc_free(proc); } else { j = ((uint64_t)p - (uint64_t)procs_new) / sizeof (pid_t); exist_arr[j] = B_TRUE; } proc = hash_next; } } for (i = 0; i < nproc_new; i++) { if (!exist_arr[i]) { if ((proc = proc_alloc()) != NULL) { proc->pid = procs_new[i]; (void) os_procfs_pname_get(proc->pid, proc->name, PROC_NAME_SIZE); (void) proc_group_add(proc); } } } s_proc_group.nprocs = nproc_new; s_proc_group.nlwps = 0; proc_traverse(proc_lwp_refresh, NULL); proc_traverse(proc_nlwps_sum, NULL); (void) pthread_mutex_unlock(&s_proc_group.mutex); free(exist_arr); } /* * Update the valid processes by scanning '/proc' */ void proc_enum_update(pid_t pid) { pid_t *procs_new; int nproc_new; if (pid > 0) { if (kill(pid, 0) == -1) { /* The process is obsolete. */ proc_obsolete(pid); } } else { if (procfs_proc_enum(&procs_new, &nproc_new) == 0) { proc_group_refresh(procs_new, nproc_new); free(procs_new); } } } /* * Increment for the refcount. */ int proc_refcount_inc(track_proc_t *proc) { int ret = -1; (void) pthread_mutex_lock(&proc->mutex); if (!proc->removing) { proc->ref_count++; ret = 0; } (void) pthread_mutex_unlock(&proc->mutex); return (ret); } /* * Decrement for the refcount. If the refcount turns to be 0 and the * 'removing' flag is set, release the 'track_proc_t' structure. */ void proc_refcount_dec(track_proc_t *proc) { boolean_t remove = B_FALSE; (void) pthread_mutex_lock(&proc->mutex); proc->ref_count--; if ((proc->ref_count == 0) && (proc->removing)) { remove = B_TRUE; } (void) pthread_mutex_unlock(&proc->mutex); if (remove) { (void) pthread_mutex_lock(&s_proc_group.mutex); proc_free(proc); (void) pthread_mutex_unlock(&s_proc_group.mutex); } } /* * Walk through all the threads in process and call 'func()' * for each thread. */ void proc_lwp_traverse(track_proc_t *proc, int (*func)(track_lwp_t *, void *, boolean_t *), void *arg) { track_lwp_t *lwp; proc_lwplist_t *list = &proc->lwp_list; boolean_t end; int i; /* * The mutex "proc->mutex" has been taken outside. */ if (list->id_arr == NULL) { return; } for (i = 0; i < list->nlwps; i++) { if ((lwp = list->id_arr[i]) != NULL) { func(lwp, arg, &end); if (end) { break; } } } } /* * Update the process's per CPU perf data. */ int proc_countval_update(track_proc_t *proc, int cpu, count_id_t count_id, uint64_t value) { count_value_t *countval, *arr_new; int cpuid_max = node_cpuid_max(); /* * Check if new cpu hotadd/online */ if (cpu >= proc->cpuid_max) { ASSERT(cpuid_max > proc->cpuid_max); if ((arr_new = realloc(proc->countval_arr, sizeof (count_value_t) * cpuid_max)) == NULL) { return (-1); } (void) memset(&arr_new[proc->cpuid_max], 0, sizeof (count_value_t) * (cpuid_max - proc->cpuid_max)); proc->countval_arr = arr_new; proc->cpuid_max = cpuid_max; } countval = &proc->countval_arr[cpu]; countval->counts[count_id] += value; return (0); } static int intval_update(track_proc_t *proc, void *arg, boolean_t *end) { int intval_ms = *((int *)arg); *end = B_FALSE; proc->intval_ms = intval_ms; lwp_intval_update(proc, intval_ms); return (0); } /* * Update with the interval of sampling. */ void proc_intval_update(int intval_ms) { (void) pthread_mutex_lock(&s_proc_group.mutex); proc_traverse(intval_update, &intval_ms); (void) pthread_mutex_unlock(&s_proc_group.mutex); } int proc_intval_get(track_proc_t *proc) { return (proc->intval_ms); } /* ARGSUSED */ static int lwp_profiling_clear(track_lwp_t *lwp, void *arg, boolean_t *end) { *end = B_FALSE; (void) memset(lwp->countval_arr, 0, sizeof (count_value_t) * lwp->cpuid_max); return (0); } /* ARGSUSED */ static int profiling_clear(track_proc_t *proc, void *arg, boolean_t *end) { *end = B_FALSE; proc_lwp_traverse(proc, lwp_profiling_clear, NULL); (void) memset(proc->countval_arr, 0, sizeof (count_value_t) * proc->cpuid_max); return (0); } void proc_profiling_clear(void) { proc_traverse(profiling_clear, NULL); } /* ARGSUSED */ static int lwp_callchain_clear(track_lwp_t *lwp, void *arg, boolean_t *end) { *end = B_FALSE; perf_countchain_reset(&lwp->count_chain); return (0); } /* ARGSUSED */ static int callchain_clear(track_proc_t *proc, void *arg, boolean_t *end) { *end = B_FALSE; proc_lwp_traverse(proc, lwp_callchain_clear, NULL); perf_countchain_reset(&proc->count_chain); return (0); } void proc_callchain_clear(void) { proc_traverse(callchain_clear, NULL); } /* ARGSUSED */ static int lwp_ll_clear(track_lwp_t *lwp, void *arg, boolean_t *end) { *end = B_FALSE; perf_llrecgrp_reset(&lwp->llrec_grp); return (0); } static int ll_clear(track_proc_t *proc, void *arg, boolean_t *end) { *end = B_FALSE; proc_lwp_traverse(proc, lwp_ll_clear, NULL); perf_llrecgrp_reset(&proc->llrec_grp); return (0); } void proc_ll_clear(track_proc_t *proc) { if (proc != NULL) { proc_lwp_traverse(proc, lwp_ll_clear, NULL); perf_llrecgrp_reset(&proc->llrec_grp); } else { proc_traverse(ll_clear, NULL); } } numatop/common/perf.c0000664000175000017500000002606312633407552014515 0ustar jinyaojinyao/* * 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 retrieve performance data */ #include #include #include #include #include #include #include #include "include/types.h" #include "include/perf.h" #include "include/proc.h" #include "include/lwp.h" #include "include/util.h" #include "include/disp.h" #include "include/os/plat.h" #include "include/os/node.h" #include "include/os/os_perf.h" static perf_ctl_t s_perf_ctl; uint64_t g_sample_period[COUNT_NUM][PRECISE_NUM] = { { SMPL_PERIOD_CORECLK_DEFAULT, SMPL_PERIOD_CORECLK_MIN, SMPL_PERIOD_CORECLK_MAX }, { SMPL_PERIOD_RMA_DEFAULT, SMPL_PERIOD_RMA_MIN, SMPL_PERIOD_RMA_MAX }, { SMPL_PERIOD_CLK_DEFAULT, SMPL_PERIOD_CLK_MIN, SMPL_PERIOD_CLK_MAX }, { SMPL_PERIOD_IR_DEFAULT, SMPL_PERIOD_IR_MIN, SMPL_PERIOD_IR_MAX }, { SMPL_PERIOD_LMA_DEFAULT, SMPL_PERIOD_LMA_MIN, SMPL_PERIOD_LMA_MAX } }; static boolean_t task_valid(perf_task_t *task) { switch (TASKID(task)) { case PERF_PROFILING_START_ID: /* fall through */ case PERF_PROFILING_SMPL_ID: /* fall through */ case PERF_PROFILING_PARTPAUSE_ID: /* fall through */ case PERF_PROFILING_RESTORE_ID: /* fall through */ case PERF_LL_START_ID: /* fall through */ case PERF_LL_SMPL_ID: /* fall through */ case PERF_CALLCHAIN_START_ID: /* fall through */ case PERF_CALLCHAIN_SMPL_ID: /* fall through */ case PERF_STOP_ID: /* fall through */ case PERF_QUIT_ID: return (B_TRUE); default: break; } return (B_FALSE); } void perf_task_set(perf_task_t *task) { (void) pthread_mutex_lock(&s_perf_ctl.mutex); (void) memcpy(&s_perf_ctl.task, task, sizeof (perf_task_t)); (void) pthread_cond_signal(&s_perf_ctl.cond); (void) pthread_mutex_unlock(&s_perf_ctl.mutex); } void perf_status_set(perf_status_t status) { (void) pthread_mutex_lock(&s_perf_ctl.status_mutex); s_perf_ctl.status = status; (void) pthread_cond_signal(&s_perf_ctl.status_cond); (void) pthread_mutex_unlock(&s_perf_ctl.status_mutex); } static boolean_t status_failed(perf_status_t status) { switch (status) { case PERF_STATUS_PROFILING_FAILED: /* fall through */ case PERF_STATUS_LL_FAILED: /* fall through */ case PERF_STATUS_CALLCHAIN_FAILED: return (B_TRUE); default: break; } return (B_FALSE); } int perf_status_wait(perf_status_t status) { struct timespec timeout; struct timeval tv; int s, ret = -1; (void) gettimeofday(&tv, NULL); timeout.tv_sec = tv.tv_sec + PERF_WAIT_NSEC; timeout.tv_nsec = tv.tv_usec * 1000; (void) pthread_mutex_lock(&s_perf_ctl.status_mutex); for (;;) { s = pthread_cond_timedwait(&s_perf_ctl.status_cond, &s_perf_ctl.status_mutex, &timeout); if (s_perf_ctl.status == status) { ret = 0; break; } if (status_failed(s_perf_ctl.status)) { break; } if (s == ETIMEDOUT) { break; } } (void) pthread_mutex_unlock(&s_perf_ctl.status_mutex); return (ret); } /* * The thread handler of 'perf thread'. */ /* ARGSUSED */ static void * perf_handler(void *arg) { perf_task_t task; int intval_ms; for (;;) { (void) pthread_mutex_lock(&s_perf_ctl.mutex); task = s_perf_ctl.task; while (!task_valid(&task)) { (void) pthread_cond_wait(&s_perf_ctl.cond, &s_perf_ctl.mutex); task = s_perf_ctl.task; } TASKID_SET(&s_perf_ctl.task, PERF_INVALID_ID); (void) pthread_mutex_unlock(&s_perf_ctl.mutex); switch (TASKID(&task)) { case PERF_QUIT_ID: debug_print(NULL, 2, "perf_handler: received QUIT\n"); os_allstop(); goto L_EXIT; case PERF_STOP_ID: os_allstop(); perf_status_set(PERF_STATUS_IDLE); break; case PERF_PROFILING_START_ID: if (os_profiling_start(&s_perf_ctl, &task) != 0) { goto L_EXIT; } break; case PERF_PROFILING_SMPL_ID: (void) os_profiling_smpl(&s_perf_ctl, &task, &intval_ms); break; case PERF_PROFILING_PARTPAUSE_ID: (void) os_profiling_partpause(&s_perf_ctl, &task); break; case PERF_PROFILING_RESTORE_ID: (void) os_profiling_restore(&s_perf_ctl, &task); break; case PERF_CALLCHAIN_START_ID: (void) os_callchain_start(&s_perf_ctl, &task); break; case PERF_CALLCHAIN_SMPL_ID: (void) os_callchain_smpl(&s_perf_ctl, &task, &intval_ms); break; case PERF_LL_START_ID: os_ll_start(&s_perf_ctl, &task); break; case PERF_LL_SMPL_ID: os_ll_smpl(&s_perf_ctl, &task, &intval_ms); break; default: break; } } L_EXIT: debug_print(NULL, 2, "perf thread is exiting.\n"); return (NULL); } /* * Initialization for perf control structure. */ int perf_init(void) { boolean_t mutex_inited = B_FALSE; boolean_t cond_inited = B_FALSE; boolean_t status_mutex_inited = B_FALSE; boolean_t status_cond_inited = B_FALSE; if (os_perf_init() != 0) { return (-1); } (void) memset(&s_perf_ctl, 0, sizeof (s_perf_ctl)); if (pthread_mutex_init(&s_perf_ctl.mutex, NULL) != 0) { goto L_EXIT; } mutex_inited = B_TRUE; if (pthread_cond_init(&s_perf_ctl.cond, NULL) != 0) { goto L_EXIT; } cond_inited = B_TRUE; if (pthread_mutex_init(&s_perf_ctl.status_mutex, NULL) != 0) { goto L_EXIT; } status_mutex_inited = B_TRUE; if (pthread_cond_init(&s_perf_ctl.status_cond, NULL) != 0) { goto L_EXIT; } status_cond_inited = B_TRUE; if (pthread_create(&s_perf_ctl.thr, NULL, perf_handler, NULL) != 0) { goto L_EXIT; } s_perf_ctl.last_ms = current_ms(); if (perf_profiling_start() != 0) { debug_print(NULL, 2, "perf_init: " "perf_profiling_start() failed\n"); goto L_EXIT; } s_perf_ctl.inited = B_TRUE; L_EXIT: if (!s_perf_ctl.inited) { if (mutex_inited) { (void) pthread_mutex_destroy(&s_perf_ctl.mutex); } if (cond_inited) { (void) pthread_cond_destroy(&s_perf_ctl.cond); } if (status_mutex_inited) { (void) pthread_mutex_destroy(&s_perf_ctl.status_mutex); } if (status_cond_inited) { (void) pthread_cond_destroy(&s_perf_ctl.status_cond); } os_perf_fini(); return (-1); } return (0); } static void perfthr_quit_wait(void) { perf_task_t task; task_quit_t *t; os_perfthr_quit_wait(); debug_print(NULL, 2, "Send PERF_QUIT_ID to perf thread\n"); (void) memset(&task, 0, sizeof (perf_task_t)); t = (task_quit_t *)&task; t->task_id = PERF_QUIT_ID; perf_task_set(&task); (void) pthread_join(s_perf_ctl.thr, NULL); debug_print(NULL, 2, "perf thread exit yet\n"); } /* * Release the resources of perf control structure. */ void perf_fini(void) { if (s_perf_ctl.inited) { perfthr_quit_wait(); (void) pthread_mutex_destroy(&s_perf_ctl.mutex); (void) pthread_cond_destroy(&s_perf_ctl.cond); (void) pthread_mutex_destroy(&s_perf_ctl.status_mutex); (void) pthread_cond_destroy(&s_perf_ctl.status_cond); s_perf_ctl.inited = B_FALSE; } os_perf_fini(); } int perf_allstop(void) { return (os_perf_allstop()); } boolean_t perf_profiling_started(void) { return (os_profiling_started(&s_perf_ctl)); } int perf_profiling_start(void) { perf_task_t task; task_profiling_t *t; (void) memset(&task, 0, sizeof (perf_task_t)); t = (task_profiling_t *)&task; t->task_id = PERF_PROFILING_START_ID; perf_task_set(&task); return (perf_status_wait(PERF_STATUS_PROFILING_STARTED)); } /* * The user may refresh the current window frequently. * One refresh operation would invoke one time perf data * sampling. If the sampling interval is too small, the * counting of an event with predefined threshold probably * doesn't get chance to overflow. Then the sampling data * is not very accurate. * * For example: * Suppose the user refreshes the window in each 100ms. The * overflow threshold for RMA is 100,000. Suppose for a * workload, it's overflowed in each 200ms. Then the user * can only see the RMA is 0 after he refreshes the window. */ void perf_smpl_wait(void) { int intval_diff; intval_diff = current_ms() - s_perf_ctl.last_ms; if (PERF_INTVAL_MIN_MS > intval_diff) { intval_diff = PERF_INTVAL_MIN_MS - intval_diff; (void) usleep(intval_diff * USEC_MS); } } int perf_profiling_smpl(void) { perf_task_t task; task_profiling_t *t; perf_smpl_wait(); (void) memset(&task, 0, sizeof (perf_task_t)); t = (task_profiling_t *)&task; t->task_id = PERF_PROFILING_SMPL_ID; perf_task_set(&task); return (0); } int perf_profiling_partpause(count_id_t count_id) { return (os_perf_profiling_partpause(count_id)); } int perf_profiling_restore(count_id_t count_id) { return (os_perf_profiling_restore(count_id)); } boolean_t perf_callchain_started(void) { if (s_perf_ctl.status == PERF_STATUS_CALLCHAIN_STARTED) { return (B_TRUE); } return (B_FALSE); } int perf_callchain_start(pid_t pid, int lwpid) { return (os_perf_callchain_start(pid, lwpid)); } int perf_callchain_smpl(void) { return (os_perf_callchain_smpl()); } boolean_t perf_ll_started(void) { if (s_perf_ctl.status == PERF_STATUS_LL_STARTED) { return (B_TRUE); } return (B_FALSE); } int perf_ll_start(pid_t pid) { perf_task_t task; task_ll_t *t; (void) memset(&task, 0, sizeof (perf_task_t)); t = (task_ll_t *)&task; t->task_id = PERF_LL_START_ID; t->pid = pid; perf_task_set(&task); return (perf_status_wait(PERF_STATUS_LL_STARTED)); } int perf_ll_smpl(pid_t pid, int lwpid) { return (os_perf_ll_smpl(&s_perf_ctl, pid, lwpid)); } void perf_llrecgrp_reset(perf_llrecgrp_t *grp) { if (grp->rec_arr != NULL) { free(grp->rec_arr); } (void) memset(grp, 0, sizeof (perf_llrecgrp_t)); } void perf_countchain_reset(perf_countchain_t *count_chain) { os_perf_countchain_reset(count_chain); } void * perf_priv_alloc(boolean_t *supported) { return (os_perf_priv_alloc(supported)); } void perf_priv_free(void *priv) { os_perf_priv_free(priv); } void perf_ll_started_set(void) { perf_status_set(PERF_STATUS_LL_STARTED); } numatop/common/reg.c0000664000175000017500000002236312633407552014335 0ustar jinyaojinyao/* * 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. */ /* * numatop uses libcurses to display data on screen. For better control, * numatop brings a new definition 'reg' (regin) to control the data on * screen. It locates where the data is, what the attributes it has * (e.g. font, color, scrolling enable?, ...). * This file contains code to handle the 'reg'. */ #include #include #include #include #include #include #include #include #include #include "include/reg.h" #include "include/types.h" #include "include/win.h" #include "include/disp.h" int g_scr_height; int g_scr_width; static boolean_t s_curses_init = B_FALSE; /* * Highlight the selected line. */ static void reg_idx_highlight(win_reg_t *r, int idx) { scroll_line_t *scroll = &r->scroll; char line[WIN_LINECHAR_MAX]; if (r->hdl != NULL) { r->line_get(r, idx, line, WIN_LINECHAR_MAX); reg_highlight_write(r, idx - scroll->page_start, ALIGN_LEFT, line); } } /* * Display the hidden lines on screen. */ static void reg_hidden_show(win_reg_t *r, int idx_start) { int i, idx_end; char line[WIN_LINECHAR_MAX]; if (r->hdl == NULL) { return; } if ((idx_end = idx_start + r->nlines_scr) > r->nlines_total) { idx_end = r->nlines_total; } reg_erase(r); for (i = idx_start; i < idx_end; i++) { r->line_get(r, i, line, WIN_LINECHAR_MAX); reg_line_write(r, i - idx_start, ALIGN_LEFT, line); } } /* * Lowlight the selected line. */ static void reg_idx_lowlight(win_reg_t *r, int idx) { scroll_line_t *scroll = &r->scroll; char line[WIN_LINECHAR_MAX]; if (r->hdl != NULL) { r->line_get(r, idx, line, WIN_LINECHAR_MAX); reg_line_write(r, idx - scroll->page_start, ALIGN_LEFT, line); } } /* * Create a 'window'. */ static WINDOW * reg_win_create(win_reg_t *r) { return (subwin(stdscr, r->nlines_scr, r->ncols_scr, r->begin_y, r->begin_x)); } /* * Initialization for one 'reg'. */ int reg_init(win_reg_t *r, int begin_x, int begin_y, int ncols, int nlines, unsigned int mode) { if ((ncols <= 0) || (nlines <= 0)) { return (-1); } (void) memset(r, 0, sizeof (win_reg_t)); r->begin_x = begin_x; r->begin_y = begin_y; r->ncols_scr = ncols; r->nlines_scr = nlines; r->mode = mode; r->hdl = reg_win_create(r); return (r->begin_y + r->nlines_scr); } /* * Initialization for the data buffer in 'reg'. */ void reg_buf_init(win_reg_t *r, void *buf, void (*line_get)(win_reg_t *, int, char *, int)) { r->buf = buf; r->line_get = line_get; } /* * Initialization for 'scrolling'. */ void reg_scroll_init(win_reg_t *r, boolean_t enable) { scroll_line_t *scroll = &r->scroll; scroll->enabled = enable; scroll->highlight = -1; } /* * Erase the data in 'reg' on screen. */ void reg_erase(win_reg_t *r) { if (r->hdl != NULL) { (void) werase(r->hdl); } } /* * Refresh the data in 'reg' and display the update data on screen. */ void reg_refresh(win_reg_t *r) { if (r->hdl != NULL) { (void) wrefresh(r->hdl); } } /* * Refresh the data in 'reg' but doesn't display the update data on * screen immediately. */ void reg_refresh_nout(win_reg_t *r) { if (r->hdl != NULL) { (void) wnoutrefresh(r->hdl); } } /* * Update the data on screen immediately. */ void reg_update_all(void) { (void) doupdate(); } /* * Free the resource of libcurses 'window'. */ void reg_win_destroy(win_reg_t *r) { if (r->hdl != NULL) { (void) delwin(r->hdl); r->hdl = NULL; } } /* * Fill data in a line and display the line on screen. */ void reg_line_write(win_reg_t *r, int line, reg_align_t align, char *content) { int pos_x = 0, len; if (r->hdl == NULL) { return; } if (r->mode != 0) { (void) wattron(r->hdl, r->mode); } len = strlen(content); if (align == ALIGN_MIDDLE) { pos_x = (r->ncols_scr - len) / 2; } if (len > 0) { (void) mvwprintw(r->hdl, line, pos_x, content); } if (r->mode != 0) { (void) wattroff(r->hdl, r->mode); } } /* * Fill data in one line and display it on screen with highlight. */ void reg_highlight_write(win_reg_t *r, int line, int align, char *content) { int pos_x = 0, len; if (r->hdl == NULL) { return; } (void) wattron(r->hdl, A_REVERSE | A_BOLD); len = strlen(content); if (align == ALIGN_MIDDLE) { pos_x = (r->ncols_scr - len) / 2; } if (len > 0) { (void) mvwprintw(r->hdl, line, pos_x, content); } (void) wattroff(r->hdl, A_REVERSE | A_BOLD); } /* * Scroll one line UP/DOWN. */ void reg_line_scroll(win_reg_t *r, int scroll_type) { scroll_line_t *scroll = &r->scroll; int highlight, idx_next; boolean_t page_scroll = B_FALSE; if ((!scroll->enabled) || (r->hdl == NULL)) { return; } if ((highlight = scroll->highlight) == -1) { highlight = 0; } if (scroll_type == SCROLL_UP) { if ((idx_next = highlight - 1) < 0) { return; } if (idx_next < scroll->page_start) { scroll->page_start--; page_scroll = B_TRUE; } } else if (scroll_type == SCROLL_DOWN) { if ((idx_next = highlight + 1) >= r->nlines_total) { return; } if (((idx_next - scroll->page_start) % r->nlines_scr) == 0) { scroll->page_start++; page_scroll = B_TRUE; } } else { return; } if (page_scroll) { reg_hidden_show(r, scroll->page_start); } reg_idx_lowlight(r, highlight); reg_idx_highlight(r, idx_next); scroll->highlight = idx_next; reg_refresh(r); } /* * Show the 'scrolling reg'. */ void reg_scroll_show(win_reg_t *r, void *lines, int nreqs, void (*str_build_func)(char *, int, int, void *)) { int highlight, i, start, end; char content[WIN_LINECHAR_MAX]; highlight = r->scroll.highlight; if (highlight != -1) { if (highlight >= r->nlines_total) { highlight = r->nlines_total - 1; } if (highlight >= r->scroll.page_start) { if ((i = ((highlight - r->scroll.page_start) / r->nlines_scr)) != 0) { r->scroll.page_start += r->nlines_scr * i; } } else { r->scroll.page_start = (highlight / r->nlines_scr) * r->nlines_scr; } start = r->scroll.page_start; i = MIN(nreqs, r->nlines_scr); if ((end = start + i) > r->nlines_total) { end = r->nlines_total; } } else { highlight = 0; start = 0; end = MIN(nreqs, r->nlines_scr); } for (i = start; i < end; i++) { str_build_func(content, sizeof (content), i, lines); dump_write("%s\n", content); if (i != highlight) { reg_line_write(r, i - r->scroll.page_start, ALIGN_LEFT, content); } } if ((highlight >= start) && (highlight < end)) { str_build_func(content, sizeof (content), highlight, lines); reg_highlight_write(r, highlight - r->scroll.page_start, ALIGN_LEFT, content); r->scroll.highlight = highlight; } } /* * Clean up the resources for libcurses. */ void reg_curses_fini(void) { if (s_curses_init) { (void) clear(); (void) refresh(); (void) endwin(); (void) fflush(stdout); (void) putchar('\r'); s_curses_init = B_FALSE; } } /* * Initialization for libcurses. */ boolean_t reg_curses_init(boolean_t first_load) { (void) initscr(); (void) refresh(); (void) use_default_colors(); (void) start_color(); (void) keypad(stdscr, TRUE); (void) nonl(); (void) cbreak(); (void) noecho(); (void) curs_set(0); getmaxyx(stdscr, g_scr_height, g_scr_width); /* * Set a window resize signal handler. */ (void) signal(SIGWINCH, disp_on_resize); s_curses_init = B_TRUE; if ((g_scr_height < 24 || g_scr_width < 80)) { if (!first_load) { (void) mvwprintw(stdscr, 0, 0, "Terminal size is too small."); (void) mvwprintw(stdscr, 1, 0, "Please resize it to 80x24 or larger."); (void) refresh(); } else { reg_curses_fini(); stderr_print("Terminal size is too small " "(resize it to 80x24 or larger).\n"); } dump_write("\n%s\n", "Terminal size is too small."); dump_write("%s\n", "Please resize it to 80x24 or larger."); return (B_FALSE); } return (B_TRUE); } numatop/common/util.c0000664000175000017500000002407012633407552014532 0ustar jinyaojinyao/* * 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 the helper routines. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "include/types.h" #include "include/util.h" #include "include/perf.h" #include "include/os/node.h" int g_pagesize; static int s_debuglevel; static FILE *s_logfile; static debug_ctl_t s_debug_ctl; static dump_ctl_t s_dump_ctl; static char s_exit_msg[EXIT_MSG_SIZE]; static dump_ctl_t s_dump_ctl; static unsigned int msdiff(struct timeval *, struct timeval *); /* * Allocate a buffer and reset it to zero. */ void * zalloc(size_t n) { void *p; if (n == 0) { return (NULL); } if ((p = malloc(n)) != NULL) { (void) memset(p, 0, n); } return (p); } /* * Initialization for debug_print component. */ int debug_init(int debug_level, FILE *log) { (void) memset(s_exit_msg, 0, EXIT_MSG_SIZE); (void) memset(&s_debug_ctl, 0, sizeof (s_debug_ctl)); if (pthread_mutex_init(&s_debug_ctl.mutex, NULL) != 0) { return (-1); } s_debug_ctl.inited = B_TRUE; s_logfile = log; s_debuglevel = debug_level; return (0); } /* * Clean up the resources for debug_print component. */ void debug_fini(void) { if (s_logfile != NULL) { (void) fclose(s_logfile); } if (s_debug_ctl.inited) { (void) pthread_mutex_destroy(&s_debug_ctl.mutex); } } /* * Write the message into log file according to different level. */ void debug_print(FILE *out, int level, const char *fmt, ...) { va_list ap; if (level <= s_debuglevel) { if (out == NULL) { if (s_logfile != NULL) { (void) pthread_mutex_lock(&s_debug_ctl.mutex); (void) fprintf(s_logfile, "%"PRIu64": ", current_ms() / 1000); va_start(ap, fmt); (void) vfprintf(s_logfile, fmt, ap); va_end(ap); (void) fflush(s_logfile); (void) pthread_mutex_unlock( &s_debug_ctl.mutex); } } else { (void) pthread_mutex_lock(&s_debug_ctl.mutex); (void) fprintf(out, "%"PRIu64": ", current_ms() / 1000); va_start(ap, fmt); (void) vfprintf(out, fmt, ap); va_end(ap); (void) fflush(out); (void) pthread_mutex_unlock(&s_debug_ctl.mutex); } } } /* * Get the current timestamp and convert it to milliseconds * (timing from numatop startup). */ uint64_t current_ms(void) { struct timeval tvnow; (void) gettimeofday(&tvnow, 0); return (msdiff(&tvnow, &g_tvbase)); } double ratio(uint64_t value1, uint64_t value2) { double r; if (value2 > 0) { r = (double)((double)value1 / (double)value2); } else { r = 0.0; } return (r); } static int procfs_walk(char *path, int **id_arr, int *num) { static DIR *dirp; struct dirent *dentp; int i = 0, size = *num, id; int *arr1 = *id_arr, *arr2; if ((dirp = opendir(path)) == NULL) { return (-1); } while ((dentp = readdir(dirp)) != NULL) { if (dentp->d_name[0] == '.') { /* skip "." and ".." */ continue; } if ((id = atoi(dentp->d_name)) == 0) { /* Not a valid pid or lwpid. */ continue; } if (i >= size) { size = size << 1; if ((arr2 = realloc(arr1, size)) == NULL) { free(arr1); *id_arr = NULL; *num = 0; return (-1); } arr1 = arr2; } arr1[i] = id; i++; } *id_arr = arr1; *num = i; (void) closedir(dirp); return (0); } int procfs_enum_id(char *path, int **id_arr, int *nids) { int *ids, num = PROCFS_ID_NUM; if ((ids = zalloc(PROCFS_ID_NUM * sizeof (int))) == NULL) { return (-1); } if (procfs_walk(path, &ids, &num) != 0) { if (ids != NULL) { free(ids); } return (-1); } *id_arr = ids; *nids = num; return (0); } /* * Retrieve the process's pid from '/proc' */ int procfs_proc_enum(pid_t **pids, int *num) { /* * It's possible that the id in return buffer is 0, * the caller needs to check again. */ return (procfs_enum_id("/proc", (int **)pids, num)); } /* * Get the interval in milliseconds between 2 timestamps. */ static unsigned int msdiff(struct timeval *tva, struct timeval *tvb) { time_t sdiff = tva->tv_sec - tvb->tv_sec; suseconds_t udiff = tva->tv_usec - tvb->tv_usec; if (sdiff < 0) { return (0); } if (udiff < 0) { udiff += MICROSEC; sdiff--; } if (sdiff < 0) { return (0); } if (sdiff >= (MAX_VALUE / MS_SEC)) { return ((unsigned int)MAX_VALUE); } return ((unsigned int)(sdiff * MS_SEC + udiff / MS_SEC)); } void exit_msg_put(const char *fmt, ...) { va_list ap; va_start(ap, fmt); (void) vsnprintf(s_exit_msg, EXIT_MSG_SIZE, fmt, ap); va_end(ap); } void exit_msg_print(void) { if (strlen(s_exit_msg) > 0) { (void) printf("%s", s_exit_msg); } } /* * Convert the CPU cycles to nanoseconds. */ uint64_t cyc2ns(uint64_t cyc) { uint64_t ns; ns = (uint64_t)(cyc * g_nsofclk); return (ns); } /* ARGSUSED */ static void cpuid(unsigned int *eax, unsigned int *ebx, unsigned int *ecx, unsigned int *edx) { #if __x86_64 __asm volatile( "cpuid\n\t" :"=a" (*eax), "=b" (*ebx), "=c" (*ecx), "=d" (*edx) :"a" (*eax)); #else __asm volatile( "push %%ebx\n\t" "cpuid\n\t" "mov %%ebx, (%4)\n\t" "pop %%ebx" :"=a" (*eax), "=c" (*ecx), "=d" (*edx) :"0" (*eax), "S" (ebx) :"memory"); #endif } /* * Get the CPU type. */ cpu_type_t cpu_type_get(void) { unsigned int eax, ebx, ecx, edx; int family, model, ext_model; cpu_type_t type = CPU_UNSUP; char vendor[16]; eax = 0; cpuid(&eax, &ebx, &ecx, &edx); (void) strncpy(&vendor[0], (char *)(&ebx), 4); (void) strncpy(&vendor[4], (char *)(&ecx), 4); (void) strncpy(&vendor[8], (char *)(&edx), 4); vendor[12] = 0; if (strncmp(vendor, "Genu" "ntel" "ineI", 12) != 0) { return (CPU_UNSUP); } eax = 1; cpuid(&eax, &ebx, &ecx, &edx); family = CPU_FAMILY(eax); model = CPU_MODEL(eax); ext_model = CPU_EXT_MODEL(eax); if (family == 6) { model = (ext_model << 4) + model; switch (model) { case 26: type = CPU_NHM_EP; break; case 44: type = CPU_WSM_EP; break; case 45: type = CPU_SNB_EP; break; case 46: type = CPU_NHM_EX; break; case 47: type = CPU_WSM_EX; break; case 62: type = CPU_IVB_EX; break; case 63: type = CPU_HSX; break; case 79: type = CPU_BDX; break; } } return (type); } /* * Initialization for dump control structure. */ int dump_init(FILE *dump_file) { (void) memset(&s_dump_ctl, 0, sizeof (s_dump_ctl)); if ((s_dump_ctl.fout = dump_file) != NULL) { if ((s_dump_ctl.cache = zalloc(DUMP_CACHE_SIZE)) == NULL) { return (-1); } s_dump_ctl.pcur = s_dump_ctl.cache; s_dump_ctl.rest_size = DUMP_CACHE_SIZE; } return (0); } /* * Clean up resources of dump control structure. */ void dump_fini(void) { if (s_dump_ctl.fout != NULL) { (void) fclose(s_dump_ctl.fout); } if (s_dump_ctl.cache != NULL) { free(s_dump_ctl.cache); } } /* * Write the message into dump file. */ void dump_write(const char *fmt, ...) { va_list ap; int n; if (s_dump_ctl.fout == NULL) { return; } if (!s_dump_ctl.cache_mode) { va_start(ap, fmt); (void) vfprintf(s_dump_ctl.fout, fmt, ap); va_end(ap); (void) fflush(s_dump_ctl.fout); } else { va_start(ap, fmt); n = vsnprintf(s_dump_ctl.pcur, s_dump_ctl.rest_size, fmt, ap); va_end(ap); s_dump_ctl.pcur += n; s_dump_ctl.rest_size -= n; } } void dump_cache_enable(void) { s_dump_ctl.cache_mode = B_TRUE; } void dump_cache_disable(void) { s_dump_ctl.cache_mode = B_FALSE; } void dump_cache_flush(void) { if (s_dump_ctl.fout != NULL) { (void) fprintf(s_dump_ctl.fout, "%s", s_dump_ctl.cache); (void) fflush(s_dump_ctl.fout); (void) memset(s_dump_ctl.cache, 0, DUMP_CACHE_SIZE); s_dump_ctl.pcur = s_dump_ctl.cache; s_dump_ctl.rest_size = DUMP_CACHE_SIZE; s_dump_ctl.cache_mode = B_FALSE; } } /* * Print the message to STDERR. */ void stderr_print(char *format, ...) { va_list ap; va_start(ap, format); /* LINTED E_SEC_PRINTF_VAR_FMT */ (void) vfprintf(stderr, format, ap); (void) fprintf(stderr, "\r"); va_end(ap); } int array_alloc(void **pp, int *ncur, int *nmax, int size, int num) { int i; void *p; if (*ncur == *nmax) { if (*pp == NULL) { if ((*pp = zalloc(num * size)) == NULL) { return (-1); } *nmax = num; } else { i = (*nmax) << 1; if ((p = realloc(*pp, i * size)) == NULL) { if (*pp != NULL) { free(*pp); *pp = NULL; } return (-1); } *pp = p; *nmax = i; } } return (0); } void pagesize_init(void) { g_pagesize = getpagesize(); } numatop/common/cmd.c0000664000175000017500000003334412633407552014324 0ustar jinyaojinyao/* * 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 handle the 'command' of NumaTOP */ #include #include #include #include #include #include #include #include "include/types.h" #include "include/cmd.h" #include "include/page.h" #include "include/win.h" #include "include/disp.h" #include "include/os/os_page.h" #include "include/os/os_cmd.h" static int s_rawnum_sortkey[] = { SORT_KEY_RMA, SORT_KEY_LMA, SORT_KEY_RL, SORT_KEY_CPI, SORT_KEY_CPU }; static int s_topnproc_sortkey[] = { SORT_KEY_RPI, SORT_KEY_LPI, SORT_KEY_RL, SORT_KEY_CPI, SORT_KEY_CPU }; static switch_t s_switch[WIN_TYPE_NUM][CMD_NUM]; static int preop_switch2profiling(cmd_t *cmd, boolean_t *smpl) { return (os_preop_switch2profiling(cmd, smpl)); } static int preop_switch2ll(cmd_t *cmd, boolean_t *smpl) { return (os_preop_switch2ll(cmd, smpl)); } static int preop_llrefresh(cmd_t *cmd, boolean_t *smpl) { return (os_preop_llrefresh(cmd, smpl)); } static int preop_llmap_get(cmd_t *cmd, boolean_t *smpl) { return (os_preop_llmap_get(cmd, smpl)); } static int preop_switch2ln(cmd_t *cmd, boolean_t *smpl) { return (os_preop_switch2ln(cmd, smpl)); } static int preop_lnrefresh(cmd_t *cmd, boolean_t *smpl) { return (os_preop_lnrefresh(cmd, smpl)); } static int preop_lnmap_get(cmd_t *cmd, boolean_t *smpl) { return (os_preop_lnmap_get(cmd, smpl)); } static int preop_back2ll(cmd_t *cmd, boolean_t *smpl) { return (os_preop_back2ll(cmd, smpl)); } static int preop_switch2callchain(cmd_t *cmd, boolean_t *smpl) { return (os_preop_switch2callchain(cmd, smpl)); } static int preop_switch2accdst(cmd_t *cmd, boolean_t *smpl) { return (os_preop_switch2accdst(cmd, smpl)); } int op_page_next(cmd_t *cmd, boolean_t smpl) { /* * Create a new page and append it after the current page in page * list. The new page is showed in page_next_execute(). */ if (page_create(cmd) != NULL) { if (page_next_execute(smpl)) { return (0); } } return (-1); } static int preop_leavecallchain(cmd_t *cmd, boolean_t *smpl) { return (os_preop_leavecallchain(cmd, smpl)); } /* ARGSUSED */ static int op_page_prev(cmd_t *cmd, boolean_t smpl) { page_t *prev; if ((prev = page_curprev_get()) != NULL) { page_drop_next(prev); (void) page_current_set(prev); page_next_set(prev); if (!page_next_execute(smpl)) { return (-1); } } return (0); } /* ARGSUSED */ int op_refresh(cmd_t *cmd, boolean_t smpl) { page_t *cur = page_current_get(); page_next_set(cur); if (!os_page_smpl_start(cur)) { /* * Refresh the current page by the latest sampling data. */ if (!page_next_execute(B_FALSE)) { return (-1); } } return (0); } static int op_llmap_stop(cmd_t *cmd, boolean_t smpl) { return (os_op_llmap_stop(cmd, smpl)); } static int op_lnmap_stop(cmd_t *cmd, boolean_t smpl) { return (os_op_lnmap_stop(cmd, smpl)); } static void sortkey_set(int cmd_id, page_t *page) { win_type_t win_type = PAGE_WIN_TYPE(page); int *arr = NULL; if (win_type == WIN_TYPE_RAW_NUM) { arr = s_rawnum_sortkey; } else { arr = s_topnproc_sortkey; } if ((cmd_id >= CMD_1_ID) && (cmd_id <= CMD_5_ID) && (arr != NULL)) { g_sortkey = arr[cmd_id - CMD_1_ID]; } } /* ARGSUSED */ static int op_sort(cmd_t *cmd, boolean_t smpl) { page_t *cur; int cmd_id; if ((cur = page_current_get()) != NULL) { cmd_id = CMD_ID(cmd); sortkey_set(cmd_id, cur); (void) op_refresh(cmd, B_FALSE); } return (0); } static int op_home(cmd_t *cmd, boolean_t smpl) { page_list_fini(); return (op_page_next(cmd, smpl)); } static int op_switch2ll(cmd_t *cmd, boolean_t smpl) { return (os_op_switch2ll(cmd, smpl)); } static int op_callchain_count(cmd_t *cmd, boolean_t smpl) { return (os_op_callchain_count(cmd, smpl)); } static int op_switch2llcallchain(cmd_t *cmd, boolean_t smpl) { return (os_op_switch2llcallchain(cmd, smpl)); } /* * Initialize for the "window switching" table. */ void switch_table_init(void) { int i; (void) memset(s_switch, 0, sizeof (s_switch)); for (i = 0; i < WIN_TYPE_NUM; i++) { s_switch[i][CMD_RESIZE_ID].op = op_refresh; s_switch[i][CMD_REFRESH_ID].op = op_refresh; s_switch[i][CMD_BACK_ID].op = op_page_prev; s_switch[i][CMD_HOME_ID].preop = preop_switch2profiling; s_switch[i][CMD_HOME_ID].op = op_home; s_switch[i][CMD_NODE_OVERVIEW_ID].preop = preop_switch2profiling; s_switch[i][CMD_NODE_OVERVIEW_ID].op = op_page_next; } /* * Initialize for window type "WIN_TYPE_RAW_NUM" */ s_switch[WIN_TYPE_RAW_NUM][CMD_BACK_ID].op = NULL; s_switch[WIN_TYPE_RAW_NUM][CMD_MONITOR_ID].op = op_page_next; s_switch[WIN_TYPE_RAW_NUM][CMD_IR_NORMALIZE_ID].op = op_page_next; s_switch[WIN_TYPE_RAW_NUM][CMD_1_ID].op = op_sort; s_switch[WIN_TYPE_RAW_NUM][CMD_2_ID].op = op_sort; s_switch[WIN_TYPE_RAW_NUM][CMD_3_ID].op = op_sort; s_switch[WIN_TYPE_RAW_NUM][CMD_4_ID].op = op_sort; s_switch[WIN_TYPE_RAW_NUM][CMD_5_ID].op = op_sort; /* * Initialize for window type "WIN_TYPE_TOPNPROC" */ s_switch[WIN_TYPE_TOPNPROC][CMD_MONITOR_ID].op = op_page_next; s_switch[WIN_TYPE_TOPNPROC][CMD_1_ID].op = op_sort; s_switch[WIN_TYPE_TOPNPROC][CMD_2_ID].op = op_sort; s_switch[WIN_TYPE_TOPNPROC][CMD_3_ID].op = op_sort; s_switch[WIN_TYPE_TOPNPROC][CMD_4_ID].op = op_sort; s_switch[WIN_TYPE_TOPNPROC][CMD_5_ID].op = op_sort; /* * Initialize for window type "WIN_TYPE_MONIPROC" */ s_switch[WIN_TYPE_MONIPROC][CMD_LAT_ID].preop = preop_switch2ll; s_switch[WIN_TYPE_MONIPROC][CMD_LAT_ID].op = op_switch2ll; s_switch[WIN_TYPE_MONIPROC][CMD_LWP_ID].op = op_page_next; s_switch[WIN_TYPE_MONIPROC][CMD_CALLCHAIN_ID].preop = preop_switch2callchain; s_switch[WIN_TYPE_MONIPROC][CMD_CALLCHAIN_ID].op = op_page_next; /* * Initialize for window type "WIN_TYPE_TOPNLWP" */ s_switch[WIN_TYPE_TOPNLWP][CMD_MONITOR_ID].op = op_page_next; /* * Initialize for window type "WIN_TYPE_MONILWP" */ s_switch[WIN_TYPE_MONILWP][CMD_LAT_ID].preop = preop_switch2ll; s_switch[WIN_TYPE_MONILWP][CMD_LAT_ID].op = op_switch2ll; s_switch[WIN_TYPE_MONILWP][CMD_CALLCHAIN_ID].preop = preop_switch2callchain; s_switch[WIN_TYPE_MONILWP][CMD_CALLCHAIN_ID].op = op_page_next; /* * Initialize for window type "WIN_TYPE_LAT_PROC" */ s_switch[WIN_TYPE_LAT_PROC][CMD_REFRESH_ID].preop = preop_llrefresh; s_switch[WIN_TYPE_LAT_PROC][CMD_BACK_ID].preop = preop_switch2profiling; s_switch[WIN_TYPE_LAT_PROC][CMD_LLCALLCHAIN_ID].op = op_switch2llcallchain; s_switch[WIN_TYPE_LAT_PROC][CMD_LATNODE_ID].preop = preop_switch2ln; s_switch[WIN_TYPE_LAT_PROC][CMD_LATNODE_ID].op = op_page_next; s_switch[WIN_TYPE_LAT_PROC][CMD_ACCDST_ID].preop = preop_switch2accdst; s_switch[WIN_TYPE_LAT_PROC][CMD_ACCDST_ID].op = op_page_next; s_switch[WIN_TYPE_LAT_PROC][CMD_MAP_GET_ID].preop = preop_llmap_get; s_switch[WIN_TYPE_LAT_PROC][CMD_MAP_GET_ID].op = op_refresh; s_switch[WIN_TYPE_LAT_PROC][CMD_MAP_STOP_ID].op = op_llmap_stop; s_switch[WIN_TYPE_LAT_PROC][CMD_NODE_OVERVIEW_ID].preop = NULL; s_switch[WIN_TYPE_LAT_PROC][CMD_NODE_OVERVIEW_ID].op = NULL; /* * Initialize for window type "WIN_TYPE_LAT_LWP" */ s_switch[WIN_TYPE_LAT_LWP][CMD_REFRESH_ID].preop = preop_llrefresh; s_switch[WIN_TYPE_LAT_LWP][CMD_BACK_ID].preop = preop_switch2profiling; s_switch[WIN_TYPE_LAT_LWP][CMD_LLCALLCHAIN_ID].op = op_switch2llcallchain; s_switch[WIN_TYPE_LAT_LWP][CMD_LATNODE_ID].preop = preop_switch2ln; s_switch[WIN_TYPE_LAT_LWP][CMD_LATNODE_ID].op = op_page_next; s_switch[WIN_TYPE_LAT_LWP][CMD_ACCDST_ID].preop = preop_switch2accdst; s_switch[WIN_TYPE_LAT_LWP][CMD_ACCDST_ID].op = op_page_next; s_switch[WIN_TYPE_LAT_LWP][CMD_MAP_GET_ID].preop = preop_llmap_get; s_switch[WIN_TYPE_LAT_LWP][CMD_MAP_GET_ID].op = op_refresh; s_switch[WIN_TYPE_LAT_LWP][CMD_MAP_STOP_ID].op = op_llmap_stop; s_switch[WIN_TYPE_LAT_LWP][CMD_NODE_OVERVIEW_ID].preop = NULL; s_switch[WIN_TYPE_LAT_LWP][CMD_NODE_OVERVIEW_ID].op = NULL; /* * Initialize for window type "WIN_TYPE_LATNODE_PROC" */ s_switch[WIN_TYPE_LATNODE_PROC][CMD_REFRESH_ID].preop = preop_lnrefresh; s_switch[WIN_TYPE_LATNODE_PROC][CMD_BACK_ID].preop = preop_back2ll; s_switch[WIN_TYPE_LATNODE_PROC][CMD_MAP_GET_ID].preop = preop_lnmap_get; s_switch[WIN_TYPE_LATNODE_PROC][CMD_MAP_GET_ID].op = op_refresh; s_switch[WIN_TYPE_LATNODE_PROC][CMD_MAP_STOP_ID].op = op_lnmap_stop; s_switch[WIN_TYPE_LATNODE_PROC][CMD_NODE_OVERVIEW_ID].preop = NULL; s_switch[WIN_TYPE_LATNODE_PROC][CMD_NODE_OVERVIEW_ID].op = NULL; /* * Initialize for window type "WIN_TYPE_LATNODE_LWP" */ s_switch[WIN_TYPE_LATNODE_LWP][CMD_REFRESH_ID].preop = preop_lnrefresh; s_switch[WIN_TYPE_LATNODE_LWP][CMD_BACK_ID].preop = preop_back2ll; s_switch[WIN_TYPE_LATNODE_LWP][CMD_MAP_GET_ID].preop = preop_lnmap_get; s_switch[WIN_TYPE_LATNODE_LWP][CMD_MAP_GET_ID].op = op_refresh; s_switch[WIN_TYPE_LATNODE_LWP][CMD_MAP_STOP_ID].op = op_lnmap_stop; s_switch[WIN_TYPE_LATNODE_LWP][CMD_NODE_OVERVIEW_ID].preop = NULL; s_switch[WIN_TYPE_LATNODE_LWP][CMD_NODE_OVERVIEW_ID].op = NULL; /* * Initialize for window type "WIN_TYPE_ACCDST_PROC" */ s_switch[WIN_TYPE_ACCDST_PROC][CMD_NODE_OVERVIEW_ID].preop = NULL; s_switch[WIN_TYPE_ACCDST_PROC][CMD_NODE_OVERVIEW_ID].op = NULL; /* * Initialize for window type "WIN_TYPE_ACCDST_LWP" */ s_switch[WIN_TYPE_ACCDST_LWP][CMD_NODE_OVERVIEW_ID].preop = NULL; s_switch[WIN_TYPE_ACCDST_LWP][CMD_NODE_OVERVIEW_ID].op = NULL; /* * Initialize for window type "WIN_TYPE_NODE_OVERVIEW" */ s_switch[WIN_TYPE_NODE_OVERVIEW][CMD_NODE_OVERVIEW_ID].preop = NULL; s_switch[WIN_TYPE_NODE_OVERVIEW][CMD_NODE_OVERVIEW_ID].op = NULL; s_switch[WIN_TYPE_NODE_OVERVIEW][CMD_BACK_ID].op = op_page_prev; s_switch[WIN_TYPE_NODE_OVERVIEW][CMD_NODE_DETAIL_ID].op = op_page_next; /* * Initialize for window type "WIN_TYPE_CALLCHAIN" */ s_switch[WIN_TYPE_CALLCHAIN][CMD_BACK_ID].preop = preop_leavecallchain;; s_switch[WIN_TYPE_CALLCHAIN][CMD_HOME_ID].preop = preop_leavecallchain;; s_switch[WIN_TYPE_CALLCHAIN][CMD_NODE_OVERVIEW_ID].preop = NULL; s_switch[WIN_TYPE_CALLCHAIN][CMD_NODE_OVERVIEW_ID].op = NULL; s_switch[WIN_TYPE_CALLCHAIN][CMD_1_ID].op = op_callchain_count; s_switch[WIN_TYPE_CALLCHAIN][CMD_2_ID].op = op_callchain_count; s_switch[WIN_TYPE_CALLCHAIN][CMD_3_ID].op = op_callchain_count; s_switch[WIN_TYPE_CALLCHAIN][CMD_4_ID].op = op_callchain_count; /* * Initialize for window type "WIN_TYPE_LLCALLCHAIN" */ s_switch[WIN_TYPE_LLCALLCHAIN][CMD_NODE_OVERVIEW_ID].preop = NULL; s_switch[WIN_TYPE_LLCALLCHAIN][CMD_NODE_OVERVIEW_ID].op = NULL; } static int callchain_id_get(void) { page_t *cur = page_current_get(); if (cur == NULL) { return (CMD_INVALID_ID); } switch (PAGE_WIN_TYPE(cur)) { case WIN_TYPE_MONIPROC: case WIN_TYPE_MONILWP: return (CMD_CALLCHAIN_ID); case WIN_TYPE_LAT_PROC: case WIN_TYPE_LAT_LWP: return (CMD_LLCALLCHAIN_ID); default: return (CMD_INVALID_ID); } } /* * Convert the character of hot-key to command id. */ int cmd_id_get(char ch) { switch (ch) { case CMD_HOME_CHAR: return (CMD_HOME_ID); case CMD_REFRESH_CHAR: return (CMD_REFRESH_ID); case CMD_QUIT_CHAR: return (CMD_QUIT_ID); case CMD_BACK_CHAR: return (CMD_BACK_ID); case CMD_LATENCY_CHAR: return (CMD_LAT_ID); case CMD_ACCDST_CHAR: return (CMD_ACCDST_ID); case CMD_IR_NORMALIZE_CHAR: return (CMD_IR_NORMALIZE_ID); case CMD_NODE_OVERVIEW_CHAR: return (CMD_NODE_OVERVIEW_ID); case CMD_CALLCHAIN_CHAR: return (callchain_id_get()); case CMD_MAP_GET_CHAR: return (CMD_MAP_GET_ID); case CMD_MAP_STOP_CHAR: return (CMD_MAP_STOP_ID); case CMD_1_CHAR: return (CMD_1_ID); case CMD_2_CHAR: return (CMD_2_ID); case CMD_3_CHAR: return (CMD_3_ID); case CMD_4_CHAR: return (CMD_4_ID); case CMD_5_CHAR: return (CMD_5_ID); default: return (CMD_INVALID_ID); } } /* * The common entry to process all commands. */ void cmd_execute(cmd_t *cmd, boolean_t *badcmd) { cmd_id_t cmd_id; win_type_t type; page_t *cur; switch_t *s; boolean_t b = B_TRUE, smpl = B_FALSE; if ((cmd_id = CMD_ID(cmd)) == CMD_INVALID_ID) { goto L_EXIT; } b = B_FALSE; if ((cur = page_current_get()) == NULL) { /* It's the first window. */ type = WIN_TYPE_RAW_NUM; } else { type = PAGE_WIN_TYPE(cur); } s = &s_switch[type][cmd_id]; if (s->preop != NULL) { (void) s->preop(cmd, &smpl); } if (s->op != NULL) { (void) s->op(cmd, smpl); } L_EXIT: if (badcmd != NULL) { *badcmd = b; } } numatop/common/numatop.c0000664000175000017500000002015112633407552015234 0ustar jinyaojinyao/* * 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 main(). */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "include/types.h" #include "include/util.h" #include "include/proc.h" #include "include/disp.h" #include "include/perf.h" #include "include/util.h" #include "include/os/plat.h" #include "include/os/node.h" #include "include/os/os_util.h" static void sigint_handler(int sig); static void print_usage(const char *exec_name); int g_ncpus; int g_sortkey; precise_type_t g_precise; pid_t g_numatop_pid; struct timeval g_tvbase; /* For automated test. */ int g_run_secs; /* * The main function. */ int main(int argc, char *argv[]) { int ret = 1, debug_level = 0; FILE *log = NULL, *dump = NULL; boolean_t locked = B_FALSE; char c; if (!os_authorized()) { return (1); } g_sortkey = SORT_KEY_CPU; g_precise = PRECISE_NORMAL; g_numatop_pid = getpid(); g_run_secs = TIME_NSEC_MAX; optind = 1; opterr = 0; /* * Parse command line arguments. */ while ((c = getopt(argc, argv, "d:l:o:f:t:hf:s:")) != EOF) { switch (c) { case 'h': print_usage(argv[0]); ret = 0; goto L_EXIT0; case 'l': debug_level = atoi(optarg); if ((debug_level < 0) || (debug_level > 2)) { stderr_print("Invalid log_level %d.\n", debug_level); print_usage(argv[0]); goto L_EXIT0; } break; case 'f': if (optarg == NULL) { stderr_print("Invalid output file.\n"); goto L_EXIT0; } if ((log = fopen(optarg, "w")) == NULL) { stderr_print("Cannot open '%s' for writing.\n", optarg); goto L_EXIT0; } break; case 's': if (optarg == NULL) { print_usage(argv[0]); goto L_EXIT0; } if (strcasecmp(optarg, "high") == 0) { g_precise = PRECISE_HIGH; break; } if (strcasecmp(optarg, "low") == 0) { g_precise = PRECISE_LOW; break; } if (strcasecmp(optarg, "normal") == 0) { g_precise = PRECISE_NORMAL; break; } stderr_print("Invalid sampling_precision '%s'.\n", optarg); print_usage(argv[0]); goto L_EXIT0; case 'd': if (optarg == NULL) { stderr_print("Invalid dump file.\n"); goto L_EXIT0; } if ((dump = fopen(optarg, "w")) == NULL) { stderr_print("Cannot open '%s' for dump.\n", optarg); goto L_EXIT0; } break; case 't': g_run_secs = atoi(optarg); if (g_run_secs <= 0) { stderr_print("Invalid run time %d.\n", g_run_secs); print_usage(argv[0]); goto L_EXIT0; } break; case ':': stderr_print("Missed argument for option %c.\n", optopt); print_usage(argv[0]); goto L_EXIT0; case '?': stderr_print("Unrecognized option %c.\n", optopt); print_usage(argv[0]); goto L_EXIT0; } } if (plat_detect() != 0) { stderr_print("CPU is not supported!\n"); ret = 2; goto L_EXIT0; } /* * Support only one numatop instance running. */ if (os_numatop_lock(&locked) != 0) { stderr_print("Fail to lock numatop!\n"); goto L_EXIT0; } if (locked) { stderr_print("Another numatop instance is running!\n"); goto L_EXIT0; } (void) gettimeofday(&g_tvbase, 0); if (debug_init(debug_level, log) != 0) { goto L_EXIT1; } log = NULL; if (dump_init(dump) != 0) { goto L_EXIT2; } dump = NULL; /* * Calculate how many nanoseconds for a TSC cycle. */ os_calibrate(); if (map_init() != 0) { goto L_EXIT3; } /* * Initialize for the "window-switching" table. */ switch_table_init(); if (proc_group_init() != 0) { goto L_EXIT4; } if (node_group_init() != 0) { stderr_print("The node/cpu number is out of range, \n" "numatop supports up to %d nodes and %d CPUs\n", NNODES_MAX, NCPUS_MAX); goto L_EXIT5; } debug_print(NULL, 2, "Detected %d online CPUs\n", g_ncpus); stderr_print("NumaTOP is starting ...\n"); if (disp_cons_ctl_init() != 0) { goto L_EXIT6; } /* * Catch signals from terminal. */ if ((signal(SIGINT, sigint_handler) == SIG_ERR) || (signal(SIGHUP, sigint_handler) == SIG_ERR) || (signal(SIGQUIT, sigint_handler) == SIG_ERR) || (signal(SIGTERM, sigint_handler) == SIG_ERR) || (signal(SIGPIPE, sigint_handler) == SIG_ERR)) { goto L_EXIT7; } /* * Initialize the perf sampling facility. */ if (perf_init() != 0) { debug_print(NULL, 2, "perf_init() is failed\n"); goto L_EXIT7; } /* * Initialize for display and create console thread & display thread. */ if (disp_init() != 0) { perf_fini(); goto L_EXIT7; } /* * Wait the disp thread to exit. The disp thread would * exit when user hits the hotkey 'Q' or press "CTRL+C". */ disp_dispthr_quit_wait(); /* * Notify cons thread to exit. */ disp_consthr_quit(); disp_fini(); stderr_print("NumaTOP is exiting ...\n"); (void) fflush(stdout); ret = 0; L_EXIT7: disp_cons_ctl_fini(); L_EXIT6: node_group_fini(); L_EXIT5: proc_group_fini(); L_EXIT4: map_fini(); L_EXIT3: dump_fini(); L_EXIT2: debug_fini(); L_EXIT1: os_numatop_unlock(); exit_msg_print(); L_EXIT0: if (dump != NULL) { (void) fclose(dump); } if (log != NULL) { (void) fclose(log); } return (ret); } /* * The signal handler. */ static void sigint_handler(int sig) { switch (sig) { case SIGINT: (void) signal(SIGINT, sigint_handler); break; case SIGHUP: (void) signal(SIGHUP, sigint_handler); break; case SIGQUIT: (void) signal(SIGQUIT, sigint_handler); break; case SIGPIPE: (void) signal(SIGPIPE, sigint_handler); break; case SIGTERM: (void) signal(SIGTERM, sigint_handler); break; default: return; } /* * It's same as the operation when user hits the hotkey 'Q'. */ disp_dispthr_quit_start(); } /* * Print command-line help information. */ static void print_usage(const char *exec_name) { char buffer[PATH_MAX]; (void) strncpy(buffer, exec_name, PATH_MAX); buffer[PATH_MAX - 1] = 0; stderr_print("Usage: %s [option(s)]\n", basename(buffer)); stderr_print( " -h print help\n" " -d path of the file to save the data in screen\n" " -l 0/1/2, the level of output warning message\n" " -f path of the file to save warning message.\n" " e.g. numatop -l 2 -f /tmp/warn.log.\n" " -s sampling precision: \n" " normal: balance precision and overhead (default)\n" " high : high sampling precision\n" " (high overhead, not recommended option)\n" " low : low sampling precision, suitable for high" " load system\n"); } numatop/common/disp.c0000664000175000017500000003677212633407552014530 0ustar jinyaojinyao/* * 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 handle the display part of NumaTOP */ #include #include #include #include #include #include #include #include #include #include #include #include #include "include/types.h" #include "include/util.h" #include "include/lwp.h" #include "include/proc.h" #include "include/disp.h" #include "include/page.h" #include "include/cmd.h" #include "include/win.h" #include "include/os/node.h" static disp_ctl_t s_disp_ctl; static cons_ctl_t s_cons_ctl; static int disp_start(void); static void* disp_handler(void *); static void* cons_handler(void *); /* * Initialization for the display control structure. */ static int disp_ctl_init(void) { (void) memset(&s_disp_ctl, 0, sizeof (s_disp_ctl)); if (pthread_mutex_init(&s_disp_ctl.mutex, NULL) != 0) { return (-1); } if (pthread_cond_init(&s_disp_ctl.cond, NULL) != 0) { (void) pthread_mutex_destroy(&s_disp_ctl.mutex); return (-1); } s_disp_ctl.inited = B_TRUE; return (0); } /* * Clean up the resources of display control structure. */ static void disp_ctl_fini(void) { if (s_disp_ctl.inited) { (void) pthread_mutex_destroy(&s_disp_ctl.mutex); (void) pthread_cond_destroy(&s_disp_ctl.cond); s_disp_ctl.inited = B_FALSE; } } /* * Initialization for the display control structure and * creating 'disp thread'. */ int disp_init(void) { if (disp_ctl_init() != 0) { return (-1); } if (disp_start() != 0) { disp_ctl_fini(); return (-1); } return (0); } /* * Before free the resources of display control structure, * make sure the 'disp thread' and 'cons thread' quit yet. */ void disp_fini(void) { disp_ctl_fini(); } /* * Initialization for the console control structure. */ int disp_cons_ctl_init(void) { (void) memset(&s_cons_ctl, 0, sizeof (s_cons_ctl)); if (pipe(s_cons_ctl.pipe) < 0) { return (-1); } s_cons_ctl.inited = B_TRUE; return (0); } /* * Clean up the resources of console control structure. */ void disp_cons_ctl_fini(void) { if (s_cons_ctl.inited) { (void) close(s_cons_ctl.pipe[0]); (void) close(s_cons_ctl.pipe[1]); s_cons_ctl.inited = B_FALSE; } } /* * Send a character '' to pipe to notify the * 'cons thread' to quit. */ void disp_consthr_quit(void) { char c; debug_print(NULL, 2, "Send PIPE_CHAR_QUIT to cons thread\n"); c = PIPE_CHAR_QUIT; if (write(s_cons_ctl.pipe[1], &c, 1) == -1) { debug_print(NULL, 2, "Fail to write PIPE_CHAR_QUIT" " to pipe\n"); } (void) pthread_join(s_cons_ctl.thr, NULL); debug_print(NULL, 2, "cons thread exit yet\n"); } static void dispthr_flagset_nolock(disp_flag_t flag) { s_disp_ctl.flag = flag; (void) pthread_cond_signal(&s_disp_ctl.cond); } static void dispthr_flagset_lock(disp_flag_t flag) { (void) pthread_mutex_lock(&s_disp_ctl.mutex); dispthr_flagset_nolock(flag); (void) pthread_mutex_unlock(&s_disp_ctl.mutex); } /* * Notify 'disp thread' that the perf profiling data is ready. */ void disp_profiling_data_ready(int intval_ms) { (void) pthread_mutex_lock(&s_disp_ctl.mutex); s_disp_ctl.intval_ms = intval_ms; dispthr_flagset_nolock(DISP_FLAG_PROFILING_DATA_READY); (void) pthread_mutex_unlock(&s_disp_ctl.mutex); } /* * Notify 'disp thread' that the perf profiling data is failed. */ void disp_profiling_data_fail(void) { dispthr_flagset_lock(DISP_FLAG_PROFILING_DATA_FAIL); } /* * Notify 'disp thread' that the perf callchain data is ready. */ void disp_callchain_data_ready(int intval_ms) { (void) pthread_mutex_lock(&s_disp_ctl.mutex); s_disp_ctl.intval_ms = intval_ms; dispthr_flagset_nolock(DISP_FLAG_CALLCHAIN_DATA_READY); (void) pthread_mutex_unlock(&s_disp_ctl.mutex); } /* * Notify 'disp thread' that the perf callchain data is failed. */ void disp_callchain_data_fail(void) { dispthr_flagset_lock(DISP_FLAG_CALLCHAIN_DATA_FAIL); } /* * Notify 'disp thread' that the perf load latency data is ready. */ void disp_ll_data_ready(int intval_ms) { (void) pthread_mutex_lock(&s_disp_ctl.mutex); s_disp_ctl.intval_ms = intval_ms; dispthr_flagset_nolock(DISP_FLAG_LL_DATA_READY); (void) pthread_mutex_unlock(&s_disp_ctl.mutex); } /* * Notify 'disp thread' that the perf load latency data is failed. */ void disp_ll_data_fail(void) { dispthr_flagset_lock(DISP_FLAG_LL_DATA_FAIL); } /* * The handler of signal 'SIGWINCH'. The function sends a 'refresh' * command to 'cons thread' to let it do a refresh operation. */ /* ARGSUSED */ void disp_on_resize(int sig) { char c = PIPE_CHAR_RESIZE; if (write(s_cons_ctl.pipe[1], &c, 1) == -1) { debug_print(NULL, 2, "Fail to write " "PIPE_CHAR_RESIZE to pipe\n"); } } void disp_intval(char *buf, int size) { (void) snprintf(buf, size, "%.1fs", (float)(s_disp_ctl.intval_ms) / (float)MS_SEC); } /* * Create 'disp thread' and 'cons thread'. */ static int disp_start(void) { if (pthread_create(&s_cons_ctl.thr, NULL, cons_handler, NULL) != 0) { debug_print(NULL, 2, "Create cons thread failed.\n"); return (-1); } if (pthread_create(&s_disp_ctl.thr, NULL, disp_handler, NULL) != 0) { debug_print(NULL, 2, "Create disp thread failed.\n"); disp_consthr_quit(); return (-1); } return (0); } static void timeout_set(struct timespec *timeout, int nsec) { struct timeval tv; (void) gettimeofday(&tv, NULL); timeout->tv_sec = tv.tv_sec + nsec; timeout->tv_nsec = tv.tv_usec * 1000; } /* * The common entry for processing command. */ static void cmd_received(cmd_t *cmd, boolean_t *quit, struct timespec *timeout) { boolean_t badcmd, execute; int cmd_id = CMD_ID(cmd); execute = B_TRUE; *quit = B_FALSE; switch (cmd_id) { case CMD_QUIT_ID: *quit = B_TRUE; return; case CMD_RESIZE_ID: /* * The screen resize signal would trigger this * command. Destroy existing screen and curses * resources and then re-init the curses resources. */ win_fix_fini(); page_win_destroy(); reg_curses_fini(); if (reg_curses_init(B_FALSE)) { win_fix_init(); } else { execute = B_FALSE; } timeout_set(timeout, DISP_DEFAULT_INTVAL); break; case CMD_REFRESH_ID: /* * User hit the hotkey 'R' to refresh current window. */ timeout_set(timeout, DISP_DEFAULT_INTVAL); break; } if (execute) { cmd_execute(cmd, &badcmd); } } /* * Called when user hits the 'UP'/'DOWN' key. */ static void key_scroll(int scroll_type) { page_t *page; dyn_win_t *dyn_win; if ((page = page_current_get()) != NULL) { dyn_win = &page->dyn_win; if (dyn_win->scroll != NULL) { (dyn_win->scroll)(dyn_win, scroll_type); } } } /* * Called when user hits the 'ENTER' key. */ static void scroll_enter(void) { page_t *page; dyn_win_t *dyn_win; if ((page = page_current_get()) != NULL) { dyn_win = &page->dyn_win; if (dyn_win->scroll_enter != NULL) { (dyn_win->scroll_enter)(dyn_win); } } } static boolean_t consthr_init_wait(void) { struct timespec timeout; disp_flag_t flag; int status = 0; timeout_set(&timeout, DISP_DEFAULT_INTVAL); (void) pthread_mutex_lock(&s_disp_ctl.mutex); flag = s_disp_ctl.flag; /* * The cons thread issues a command to go to home window * when it completes the initialization. */ while ((flag != DISP_FLAG_CMD) && (flag != DISP_FLAG_QUIT)) { status = pthread_cond_timedwait(&s_disp_ctl.cond, &s_disp_ctl.mutex, &timeout); if (status == ETIMEDOUT) { break; } flag = s_disp_ctl.flag; } (void) pthread_mutex_unlock(&s_disp_ctl.mutex); if ((status == ETIMEDOUT) || (flag == DISP_FLAG_QUIT)) { return (B_FALSE); } return (B_TRUE); } /* * The handler of 'disp thread'. */ /* ARGSUSED */ static void * disp_handler(void *arg) { disp_flag_t flag; int status = 0; cmd_t cmd; boolean_t quit, pagelist_inited = B_FALSE; struct timespec timeout; uint64_t start_ms; int64_t diff_ms; /* * Wait cons thread to complete initialization. */ if (!consthr_init_wait()) { debug_print(NULL, 2, "Timeout for waiting cons thread to " "complete initialization\n"); /* * The cons thread should exit with error or startup failed, * disp thread stops running. */ goto L_EXIT; } /* * NumaTOP contains multiple windows. It uses double linked list * to link all of windows. */ page_list_init(); pagelist_inited = B_TRUE; timeout_set(&timeout, 0); start_ms = current_ms(); for (;;) { status = 0; (void) pthread_mutex_lock(&s_disp_ctl.mutex); flag = s_disp_ctl.flag; while (flag == DISP_FLAG_NONE) { status = pthread_cond_timedwait(&s_disp_ctl.cond, &s_disp_ctl.mutex, &timeout); flag = s_disp_ctl.flag; if (status == ETIMEDOUT) { break; } } if (flag == DISP_FLAG_CMD) { (void) memcpy(&cmd, &s_disp_ctl.cmd, sizeof (cmd)); } s_disp_ctl.flag = DISP_FLAG_NONE; (void) pthread_mutex_unlock(&s_disp_ctl.mutex); diff_ms = current_ms() - start_ms; if (g_run_secs <= diff_ms / MS_SEC) { g_run_secs = TIME_NSEC_MAX; debug_print(NULL, 2, "disp: it's time to exit\n"); continue; } if ((status == ETIMEDOUT) && (flag == DISP_FLAG_NONE)) { if (page_current_get() == NULL) { timeout_set(&timeout, DISP_DEFAULT_INTVAL); continue; } /* * Force a 'refresh' operation. */ CMD_ID_SET(&cmd, CMD_REFRESH_ID); cmd_execute(&cmd, NULL); timeout_set(&timeout, DISP_DEFAULT_INTVAL); continue; } switch (flag) { case DISP_FLAG_QUIT: debug_print(NULL, 2, "disp: received DISP_FLAG_QUIT\n"); goto L_EXIT; case DISP_FLAG_CMD: cmd_received(&cmd, &quit, &timeout); if (quit) { debug_print(NULL, 2, "disp thread received CMD_QUIT_ID\n"); goto L_EXIT; } break; case DISP_FLAG_PROFILING_DATA_READY: case DISP_FLAG_CALLCHAIN_DATA_READY: case DISP_FLAG_LL_DATA_READY: /* * Show the page. */ (void) page_next_execute(B_FALSE); timeout_set(&timeout, DISP_DEFAULT_INTVAL); break; case DISP_FLAG_PROFILING_DATA_FAIL: case DISP_FLAG_CALLCHAIN_DATA_FAIL: case DISP_FLAG_LL_DATA_FAIL: /* * Received the notification that the perf counting * was failed. */ debug_print(NULL, 2, "disp: profiling/callchain/LL data failed.\n"); disp_go_home(); break; case DISP_FLAG_SCROLLUP: /* * User hits the "UP" key. */ key_scroll(SCROLL_UP); if (status == ETIMEDOUT) { timeout_set(&timeout, DISP_DEFAULT_INTVAL); } break; case DISP_FLAG_SCROLLDOWN: /* * User hits the "DOWN" key. */ key_scroll(SCROLL_DOWN); if (status == ETIMEDOUT) { timeout_set(&timeout, DISP_DEFAULT_INTVAL); } break; case DISP_FLAG_SCROLLENTER: /* * User selects a scroll item and hit the "ENTER". */ scroll_enter(); if (status == ETIMEDOUT) { timeout_set(&timeout, DISP_DEFAULT_INTVAL); } break; default: break; } } L_EXIT: if (pagelist_inited) { page_list_fini(); } /* * Let the perf thread exit first. */ perf_fini(); debug_print(NULL, 2, "disp thread is exiting\n"); return (NULL); } /* * The handler of 'cons thread' */ /* ARGSUSED */ static void * cons_handler(void *arg) { int c, cmd_id; unsigned char ch; if (!reg_curses_init(B_TRUE)) { goto L_EXIT; } win_fix_init(); /* * Excute "home" command. It shows the NumaTop default page. */ disp_go_home(); for (;;) { FD_ZERO(&s_cons_ctl.fds); FD_SET(STDIN_FILENO, &s_cons_ctl.fds); FD_SET(s_cons_ctl.pipe[0], &s_cons_ctl.fds); /* * Wait one character from "stdin" or pipe. */ if (select(s_cons_ctl.pipe[0] + 1, &s_cons_ctl.fds, NULL, NULL, NULL) > 0) { if (FD_ISSET(s_cons_ctl.pipe[0], &s_cons_ctl.fds)) { if (read(s_cons_ctl.pipe[0], &ch, 1) == 1) { /* * Character is from pipe. */ if (ch == PIPE_CHAR_QUIT) { /* * Received a QUIT notification, * "console thread" will be quit */ debug_print(NULL, 2, "cons: " "received PIPE_CHAR_QUIT\n"); break; } if (ch == PIPE_CHAR_RESIZE) { /* * Send the "RESIZE" command * to "display thread". */ (void) pthread_mutex_lock( &s_disp_ctl.mutex); CMD_ID_SET(&s_disp_ctl.cmd, CMD_RESIZE_ID); dispthr_flagset_nolock(DISP_FLAG_CMD); (void) pthread_mutex_unlock( &s_disp_ctl.mutex); } } } else { /* * Character is from STDIN. */ if ((c = getch()) == ERR) { /* * It's possile if the associated * terminal is lost. */ debug_print(NULL, 2, "cons: " "getch() failed.\n"); break; } ch = tolower((unsigned char)c); dump_write("\n<-- User hit the key '%c' " "(ascii = %d) -->\n", ch, (int)ch); cmd_id = cmd_id_get(ch); if (cmd_id != CMD_INVALID_ID) { /* * The character is a command. Send * the command to 'disp thread'. */ (void) pthread_mutex_lock( &s_disp_ctl.mutex); CMD_ID_SET(&s_disp_ctl.cmd, cmd_id); dispthr_flagset_nolock(DISP_FLAG_CMD); (void) pthread_mutex_unlock( &s_disp_ctl.mutex); } else { /* * Hit the keys 'UP'/'DOWN'/'ENTER' */ switch (ch) { case 2: /* KEY DOWN */ dispthr_flagset_lock( DISP_FLAG_SCROLLDOWN); break; case 3: /* KEY UP */ dispthr_flagset_lock( DISP_FLAG_SCROLLUP); break; case 13: /* enter. */ dispthr_flagset_lock( DISP_FLAG_SCROLLENTER); break; default: break; } } } } } reg_curses_fini(); L_EXIT: debug_print(NULL, 2, "cons thread is exiting\n"); return (NULL); } void disp_dispthr_quit_wait(void) { (void) pthread_join(s_disp_ctl.thr, NULL); debug_print(NULL, 2, "disp thread exit yet\n"); } void disp_dispthr_quit_start(void) { (void) pthread_mutex_lock(&s_disp_ctl.mutex); CMD_ID_SET(&s_disp_ctl.cmd, CMD_QUIT_ID); dispthr_flagset_nolock(DISP_FLAG_CMD); (void) pthread_mutex_unlock(&s_disp_ctl.mutex); } /* * Send the 'HOME' command to 'disp thread'. */ void disp_go_home(void) { (void) pthread_mutex_lock(&s_disp_ctl.mutex); CMD_ID_SET(&s_disp_ctl.cmd, CMD_HOME_ID); dispthr_flagset_nolock(DISP_FLAG_CMD); (void) pthread_mutex_unlock(&s_disp_ctl.mutex); } numatop/common/include/0000775000175000017500000000000012633413457015032 5ustar jinyaojinyaonumatop/common/include/types.h0000664000175000017500000000723412633407552016354 0ustar jinyaojinyao/* * 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. */ #ifndef _NUMATOP_TYPES_H #define _NUMATOP_TYPES_H #include "./os/os_types.h" #ifdef __cplusplus extern "C" { #endif typedef enum { PRECISE_NORMAL = 0, PRECISE_HIGH, PRECISE_LOW } precise_type_t; #define PRECISE_NUM 3 typedef enum { CPU_UNSUP = 0, CPU_WSM_EX, CPU_SNB_EP, CPU_NHM_EX, CPU_NHM_EP, CPU_WSM_EP, CPU_IVB_EX, CPU_HSX, CPU_BDX } cpu_type_t; #define CPU_TYPE_NUM 9 typedef enum { SORT_KEY_INVALID = -1, SORT_KEY_CPU = 0, SORT_KEY_PID, SORT_KEY_RPI, SORT_KEY_LPI, SORT_KEY_CPI, SORT_KEY_RMA, SORT_KEY_LMA, SORT_KEY_RL } sort_key_t; #define MAX_VALUE 4294967295U #define NS_SEC 1000000000 #define MS_SEC 1000 #define NS_USEC 1000 #define USEC_MS 1000 #define NS_MS 1000000 #define MICROSEC 1000000 #define GHZ 1000000000 #define MHZ 1000000 #define KHZ 1000 #define GB_BYTES 1024*1024*1024 #define KB_BYTES 1024 #define TIME_NSEC_MAX 2147483647 #ifndef PATH_MAX #define PATH_MAX 2048 #endif #define SCRIPT_SIZE 4096 #define SMPL_PERIOD_INFINITE 0XFFFFFFFFFFFFFFULL #define SMPL_PERIOD_RMA_DEFAULT 10000 #define SMPL_PERIOD_LMA_DEFAULT 10000 #define SMPL_PERIOD_CLK_DEFAULT 10000000 #define SMPL_PERIOD_CORECLK_DEFAULT SMPL_PERIOD_INFINITE #define SMPL_PERIOD_IR_DEFAULT 10000000 #define SMPL_PERIOD_RMA_MIN 5000 #define SMPL_PERIOD_LMA_MIN 5000 #define SMPL_PERIOD_CLK_MIN 1000000 #define SMPL_PERIOD_CORECLK_MIN SMPL_PERIOD_INFINITE #define SMPL_PERIOD_IR_MIN 1000000 #define SMPL_PERIOD_RMA_MAX 100000 #define SMPL_PERIOD_LMA_MAX 100000 #define SMPL_PERIOD_CLK_MAX 100000000 #define SMPL_PERIOD_CORECLK_MAX SMPL_PERIOD_INFINITE #define SMPL_PERIOD_IR_MAX 100000000 typedef enum { COUNT_INVALID = -1, COUNT_CORE_CLK = 0, COUNT_RMA, COUNT_CLK, COUNT_IR, COUNT_LMA } count_id_t; #define COUNT_NUM 5 #define NNODES_MAX 64 #define NCPUS_NODE_MAX 64 #define NCPUS_MAX (NNODES_MAX * NCPUS_NODE_MAX) #define NPROCS_NAX 4096 #define LL_THRESH 128 #define LL_PERIOD 1000 typedef struct _count_value { uint64_t counts[COUNT_NUM]; } count_value_t; typedef struct _bufaddr { uint64_t addr; uint64_t size; } bufaddr_t; #ifdef __cplusplus } #endif #endif /* _NUMATOP_TYPES_H */ numatop/common/include/os/0000775000175000017500000000000012633407552015452 5ustar jinyaojinyaonumatop/common/include/os/plat.h0000664000175000017500000000507112633407552016566 0ustar jinyaojinyao/* * 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. */ #ifndef _NUMATOP_PLAT_H #define _NUMATOP_PLAT_H #include #include #include "../types.h" #ifdef __cplusplus extern "C" { #endif #define PLAT_EVENT_DESC_SIZE 64 typedef struct _plat_event_config { uint32_t type; /* * config = "code + umask" if type is PERF_TYPE_RAW or * event_id if type is PERF_TYPE_HARDWARE. */ uint64_t config; uint64_t other_attr; uint64_t extra_value; char desc[PLAT_EVENT_DESC_SIZE]; } plat_event_config_t; extern uint64_t g_sample_period[COUNT_NUM][PRECISE_NUM]; typedef void (*pfn_plat_profiling_config_t)(count_id_t, plat_event_config_t *); typedef void (*pfn_plat_ll_config_t)(plat_event_config_t *); typedef int (*pfn_plat_offcore_num_t)(void); extern int plat_detect(void); extern void plat_profiling_config(count_id_t, plat_event_config_t *); extern void plat_ll_config(plat_event_config_t *); extern void plat_config_get(count_id_t, plat_event_config_t *, plat_event_config_t *); extern int plat_offcore_num(void); #ifdef __cplusplus } #endif #endif /* _NUMATOP_PLAT_H */ numatop/common/include/os/os_page.h0000664000175000017500000000345312633407552017245 0ustar jinyaojinyao/* * 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. */ #ifndef _NUMATOP_OS_PAGE_H #define _NUMATOP_OS_PAGE_H #include #include "../types.h" #include "../page.h" #ifdef __cplusplus extern "C" { #endif extern boolean_t os_page_smpl_start(page_t *); #ifdef __cplusplus } #endif #endif /* _NUMATOP_OS_PAGE_H */ numatop/common/include/os/os_util.h0000664000175000017500000000530712633407552017306 0ustar jinyaojinyao/* * 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. */ #ifndef _NUMATOP_OS_UTIL_H #define _NUMATOP_OS_UTIL_H #include #include #include #include #include #include #include "../types.h" #include "../os/node.h" #ifdef __cplusplus extern "C" { #endif #define DIGIT_LEN_MAX 512 #define CPU0_CPUFREQ_PATH "/sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq" #define NODE_INFO_ROOT "/sys/devices/system/node/" #define NODE_NONLINE_PATH "/sys/devices/system/node/online" #define CPUINFO_PATH "/proc/cpuinfo" extern boolean_t os_authorized(void); extern int os_numatop_lock(boolean_t *); extern void os_numatop_unlock(void); extern int os_procfs_psinfo_get(pid_t, void *); extern int os_procfs_pname_get(pid_t, char *, int); extern int os_procfs_lwp_enum(pid_t, int **lwps, int *); extern boolean_t os_procfs_lwp_valid(pid_t, int); extern void os_calibrate(void); extern boolean_t os_sysfs_node_enum(int *, int, int *); extern boolean_t os_sysfs_cpu_enum(int, int *, int, int *); extern int sysfs_os_online_ncpus(void); extern boolean_t os_sysfs_meminfo(int, node_meminfo_t *); extern int os_sysfs_online_ncpus(void); #ifdef __cplusplus } #endif #endif /* _NUMATOP_OS_UTIL_H */ numatop/common/include/os/sym.h0000664000175000017500000000765612633407552016451 0ustar jinyaojinyao/* * 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. */ #ifndef _NUMATOP_SYM_H #define _NUMATOP_SYM_H #include #include #include #include "../types.h" #ifdef __cplusplus extern "C" { #endif /* * This symbol type is the same as STT_FUNC except that it always * points to a function or piece of executable code which takes no * arguments and which returns a function pointer. */ #ifndef STT_IFUNC #define STT_IFUNC 10 #endif #define SYM_ITEM_NUM 1024 #define SYM_NAME_SIZE 32 #define SYM_LIB_NUM 16 #define SYM_CLASS_NUM 2 #define ELF32_LOAD_ADDR 0x08048000 #define ELF64_LOAD_ADDR 0x400000 #define INVALID_LOADADDR (uint64_t)(-1) typedef enum { SYM_CLASS_INVALID = -1, SYM_CLASS_ELF32 = 0, SYM_CLASS_ELF64 } sym_class_t; typedef enum { SYM_TYPE_FUNC = 1, SYM_TYPE_OBJECT } sym_type_t; typedef struct _sym_item { char name[SYM_NAME_SIZE]; sym_type_t type; unsigned int index; uint64_t off; uint64_t size; } sym_item_t; typedef struct _sym_binary { sym_item_t *items; int nitem_cur; int nitem_max; FILE *fp; char path[PATH_MAX]; } sym_binary_t; typedef struct _sym_lib { sym_binary_t binary; struct _sym_lib *next; } sym_lib_t; typedef struct _sym_libref { sym_lib_t **libs; uint64_t *lib_loadaddr; int nlib_cur; int nlib_max; } sym_libref_t; typedef struct _sym { sym_binary_t image; uint64_t image_loadaddr; sym_libref_t libref; boolean_t loaded; } sym_t; typedef struct _sym_ops { int (*pfn_binary_read)(sym_binary_t *, sym_type_t); } sym_ops_t; typedef struct _sym_callentry { uint64_t addr; uint64_t size; char name[SYM_NAME_SIZE]; } sym_callentry_t; typedef struct _sym_callchain { sym_callentry_t *entry_arr; int nentry; int naccess; struct _sym_callchain *prev; struct _sym_callchain *next; } sym_callchain_t; typedef struct _sym_chainlist { sym_callchain_t *head; sym_callchain_t *tail; int num; } sym_chainlist_t; #define IP_HIT(ip, addr, size) \ (((ip) >= (addr)) && ((ip) < (addr) + (size))) struct _track_proc; void sym_init(void); void sym_fini(void); int sym_load(struct _track_proc *, sym_type_t); void sym_free(sym_t *); int sym_callchain_add(sym_t *, uint64_t *, int, sym_chainlist_t *); void sym_callchain_resort(sym_chainlist_t *); sym_callchain_t* sym_callchain_detach(sym_chainlist_t *); void sym_callchain_free(sym_callchain_t *); void sym_chainlist_free(sym_chainlist_t *); int sym_chainlist_nentry(sym_chainlist_t *, int *); #ifdef __cplusplus } #endif #endif /* _NUMATOP_SYM_H */ numatop/common/include/os/node.h0000664000175000017500000000637512633407552016563 0ustar jinyaojinyao/* * 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. */ #ifndef _NUMATOP_NODE_H #define _NUMATOP_NODE_H #include #include #include #include "../types.h" #include "../perf.h" #ifdef __cplusplus extern "C" { #endif #define NODE_ALL -1 #define INVALID_NID -1 #define INVALID_CPUID -1 #define NODE_VALID(node) ((node)->nid != INVALID_NID) typedef struct _node_meminfo { uint64_t mem_total; uint64_t mem_free; uint64_t active; uint64_t inactive; uint64_t dirty; uint64_t writeback; uint64_t mapped; } node_meminfo_t; typedef struct _node { int nid; int ncpus; perf_cpu_t cpus[NCPUS_NODE_MAX]; count_value_t countval; node_meminfo_t meminfo; boolean_t hotadd; boolean_t hotremove; } node_t; typedef struct _node_group { pthread_mutex_t mutex; node_t nodes[NNODES_MAX]; int nnodes; int cpuid_max; int intval_ms; boolean_t inited; } node_group_t; extern int node_group_init(void); extern void node_group_fini(void); extern int node_group_refresh(boolean_t); extern node_t *node_get(int); extern int node_num(void); extern void node_group_lock(void); extern void node_group_unlock(void); extern node_t *node_by_cpu(int); extern int node_ncpus(node_t *); extern int node_intval_get(void); extern void node_countval_update(node_t *, count_id_t, uint64_t); extern uint64_t node_countval_get(node_t *, count_id_t); extern void node_meminfo(int, node_meminfo_t *); extern int node_cpu_traverse(pfn_perf_cpu_op_t, void *, boolean_t, pfn_perf_cpu_op_t); extern uint64_t node_countval_sum(count_value_t *, int, int, count_id_t); extern perf_cpu_t* node_cpus(node_t *); extern void node_intval_update(int); extern void node_profiling_clear(void); extern node_t* node_valid_get(int); extern int node_cpuid_max(void); #ifdef __cplusplus } #endif #endif /* _NODE_H */ numatop/common/include/os/os_perf.h0000664000175000017500000001015612633407552017263 0ustar jinyaojinyao/* * 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. */ #ifndef _NUMATOP_OS_PERF_H #define _NUMATOP_OS_PERF_H #include #include #include #include "../types.h" #ifdef __cplusplus extern "C" { #endif #define PERF_REC_NUM 512 #define PERF_FD_NUM NCPUS_MAX * COUNT_NUM #define INVALID_CODE_UMASK (uint64_t)(-1) typedef struct _os_perf_callchain { unsigned int ip_num; uint64_t ips[IP_NUM]; } os_perf_callchain_t; typedef struct _os_perf_llrec { uint64_t addr; uint64_t cpu; uint64_t latency; os_perf_callchain_t callchain; } os_perf_llrec_t; typedef struct _perf_cpu { int cpuid; int fds[COUNT_NUM]; int group_idx; int map_len; int map_mask; void *map_base; boolean_t hit; boolean_t hotadd; boolean_t hotremove; count_value_t countval_last; } perf_cpu_t; typedef int (*pfn_perf_cpu_op_t)(struct _perf_cpu *, void *); struct _perf_ctl; union _perf_task; struct _perf_countchain; struct _perf_chainrecgrp; struct _perf_chainrec; struct _perf_llrecgrp; extern boolean_t os_profiling_started(struct _perf_ctl *); extern int os_profiling_start(struct _perf_ctl *, union _perf_task *); extern int os_profiling_smpl(struct _perf_ctl *, union _perf_task *, int *); extern int os_profiling_partpause(struct _perf_ctl *, union _perf_task *); extern int os_profiling_restore(struct _perf_ctl *, union _perf_task *); extern int os_callchain_start(struct _perf_ctl *, union _perf_task *); extern int os_callchain_smpl(struct _perf_ctl *, union _perf_task *, int *); extern int os_ll_start(struct _perf_ctl *, union _perf_task *); extern int os_ll_smpl(struct _perf_ctl *, union _perf_task *, int *); extern int os_perf_init(void); extern void os_perf_fini(void); extern void os_perfthr_quit_wait(void); extern int os_perf_profiling_partpause(count_id_t); extern int os_perf_profiling_restore(count_id_t); extern int os_perf_callchain_start(pid_t, int); extern int os_perf_callchain_smpl(void); extern int os_perf_ll_smpl(struct _perf_ctl *, pid_t, int); extern void os_perf_countchain_reset(struct _perf_countchain *); extern void os_allstop(void); extern int os_perf_allstop(void); extern void* os_perf_priv_alloc(boolean_t *); extern void os_perf_priv_free(void *); extern int os_perf_chain_nentries( struct _perf_chainrecgrp *, int *); extern struct _perf_chainrec* os_perf_chainrec_get( struct _perf_chainrecgrp *, int); extern char *os_perf_chain_entryname(void *, int); extern void os_perf_cpuarr_init(perf_cpu_t *, int, boolean_t); extern void os_perf_cpuarr_fini(perf_cpu_t *, int, boolean_t); extern int os_perf_cpuarr_refresh(perf_cpu_t *, int, int *, int, boolean_t); #ifdef __cplusplus } #endif #endif /* _NUMATOP_OS_PERF_H */ numatop/common/include/os/os_cmd.h0000664000175000017500000000515312633407552017073 0ustar jinyaojinyao/* * 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. */ #ifndef _NUMATOP_OS_CMD_H #define _NUMATOP_OS_CMD_H #include #include #include "../types.h" #include "../cmd.h" #ifdef __cplusplus extern "C" { #endif extern int os_preop_switch2profiling(cmd_t *, boolean_t *); extern int os_preop_switch2ll(cmd_t *, boolean_t *); extern int os_preop_llrefresh(cmd_t *, boolean_t *); extern int os_preop_llmap_get(cmd_t *, boolean_t *); extern int os_preop_switch2ln(cmd_t *, boolean_t *); extern int os_preop_lnrefresh(cmd_t *, boolean_t *); extern int os_preop_lnmap_get(cmd_t *, boolean_t *); extern int os_preop_back2ll(cmd_t *, boolean_t *); extern int os_preop_switch2callchain(cmd_t *, boolean_t *); extern int os_preop_switch2accdst(cmd_t *, boolean_t *); extern int os_preop_leavecallchain(cmd_t *, boolean_t *); extern int os_op_llmap_stop(cmd_t *, boolean_t); extern int os_op_lnmap_stop(cmd_t *, boolean_t); extern int os_op_switch2ll(cmd_t *, boolean_t); extern int os_op_callchain_count(cmd_t *, boolean_t); extern int os_op_switch2llcallchain(cmd_t *, boolean_t); #ifdef __cplusplus } #endif #endif /* _NUMATOP_OS_CMD_H */ numatop/common/include/os/linux/0000775000175000017500000000000012633407552016611 5ustar jinyaojinyaonumatop/common/include/os/linux/perf_event.h0000664000175000017500000004476512633407552021137 0ustar jinyaojinyao/* * Performance events: * * Copyright (C) 2008-2009, Thomas Gleixner * Copyright (C) 2008-2011, Red Hat, Inc., Ingo Molnar * Copyright (C) 2008-2011, Red Hat, Inc., Peter Zijlstra * * Data type definitions, declarations, prototypes. * * Started by: Thomas Gleixner and Ingo Molnar * * For licencing details see kernel-base/COPYING */ #ifndef _UAPI_LINUX_PERF_EVENT_H #define _UAPI_LINUX_PERF_EVENT_H #include #include #include /* * User-space ABI bits: */ /* * attr.type */ enum perf_type_id { PERF_TYPE_HARDWARE = 0, PERF_TYPE_SOFTWARE = 1, PERF_TYPE_TRACEPOINT = 2, PERF_TYPE_HW_CACHE = 3, PERF_TYPE_RAW = 4, PERF_TYPE_BREAKPOINT = 5, PERF_TYPE_MAX, /* non-ABI */ }; /* * Generalized performance event event_id types, used by the * attr.event_id parameter of the sys_perf_event_open() * syscall: */ enum perf_hw_id { /* * Common hardware events, generalized by the kernel: */ PERF_COUNT_HW_CPU_CYCLES = 0, PERF_COUNT_HW_INSTRUCTIONS = 1, PERF_COUNT_HW_CACHE_REFERENCES = 2, PERF_COUNT_HW_CACHE_MISSES = 3, PERF_COUNT_HW_BRANCH_INSTRUCTIONS = 4, PERF_COUNT_HW_BRANCH_MISSES = 5, PERF_COUNT_HW_BUS_CYCLES = 6, PERF_COUNT_HW_STALLED_CYCLES_FRONTEND = 7, PERF_COUNT_HW_STALLED_CYCLES_BACKEND = 8, PERF_COUNT_HW_REF_CPU_CYCLES = 9, PERF_COUNT_HW_MAX, /* non-ABI */ }; /* * Generalized hardware cache events: * * { L1-D, L1-I, LLC, ITLB, DTLB, BPU, NODE } x * { read, write, prefetch } x * { accesses, misses } */ enum perf_hw_cache_id { PERF_COUNT_HW_CACHE_L1D = 0, PERF_COUNT_HW_CACHE_L1I = 1, PERF_COUNT_HW_CACHE_LL = 2, PERF_COUNT_HW_CACHE_DTLB = 3, PERF_COUNT_HW_CACHE_ITLB = 4, PERF_COUNT_HW_CACHE_BPU = 5, PERF_COUNT_HW_CACHE_NODE = 6, PERF_COUNT_HW_CACHE_MAX, /* non-ABI */ }; enum perf_hw_cache_op_id { PERF_COUNT_HW_CACHE_OP_READ = 0, PERF_COUNT_HW_CACHE_OP_WRITE = 1, PERF_COUNT_HW_CACHE_OP_PREFETCH = 2, PERF_COUNT_HW_CACHE_OP_MAX, /* non-ABI */ }; enum perf_hw_cache_op_result_id { PERF_COUNT_HW_CACHE_RESULT_ACCESS = 0, PERF_COUNT_HW_CACHE_RESULT_MISS = 1, PERF_COUNT_HW_CACHE_RESULT_MAX, /* non-ABI */ }; /* * Special "software" events provided by the kernel, even if the hardware * does not support performance events. These events measure various * physical and sw events of the kernel (and allow the profiling of them as * well): */ enum perf_sw_ids { PERF_COUNT_SW_CPU_CLOCK = 0, PERF_COUNT_SW_TASK_CLOCK = 1, PERF_COUNT_SW_PAGE_FAULTS = 2, PERF_COUNT_SW_CONTEXT_SWITCHES = 3, PERF_COUNT_SW_CPU_MIGRATIONS = 4, PERF_COUNT_SW_PAGE_FAULTS_MIN = 5, PERF_COUNT_SW_PAGE_FAULTS_MAJ = 6, PERF_COUNT_SW_ALIGNMENT_FAULTS = 7, PERF_COUNT_SW_EMULATION_FAULTS = 8, PERF_COUNT_SW_MAX, /* non-ABI */ }; /* * Bits that can be set in attr.sample_type to request information * in the overflow packets. */ enum perf_event_sample_format { PERF_SAMPLE_IP = 1U << 0, PERF_SAMPLE_TID = 1U << 1, PERF_SAMPLE_TIME = 1U << 2, PERF_SAMPLE_ADDR = 1U << 3, PERF_SAMPLE_READ = 1U << 4, PERF_SAMPLE_CALLCHAIN = 1U << 5, PERF_SAMPLE_ID = 1U << 6, PERF_SAMPLE_CPU = 1U << 7, PERF_SAMPLE_PERIOD = 1U << 8, PERF_SAMPLE_STREAM_ID = 1U << 9, PERF_SAMPLE_RAW = 1U << 10, PERF_SAMPLE_BRANCH_STACK = 1U << 11, PERF_SAMPLE_REGS_USER = 1U << 12, PERF_SAMPLE_STACK_USER = 1U << 13, PERF_SAMPLE_WEIGHT = 1U << 14, PERF_SAMPLE_DATA_SRC = 1U << 15, PERF_SAMPLE_MAX = 1U << 16, /* non-ABI */ }; /* * values to program into branch_sample_type when PERF_SAMPLE_BRANCH is set * * If the user does not pass priv level information via branch_sample_type, * the kernel uses the event's priv level. Branch and event priv levels do * not have to match. Branch priv level is checked for permissions. * * The branch types can be combined, however BRANCH_ANY covers all types * of branches and therefore it supersedes all the other types. */ enum perf_branch_sample_type { PERF_SAMPLE_BRANCH_USER = 1U << 0, /* user branches */ PERF_SAMPLE_BRANCH_KERNEL = 1U << 1, /* kernel branches */ PERF_SAMPLE_BRANCH_HV = 1U << 2, /* hypervisor branches */ PERF_SAMPLE_BRANCH_ANY = 1U << 3, /* any branch types */ PERF_SAMPLE_BRANCH_ANY_CALL = 1U << 4, /* any call branch */ PERF_SAMPLE_BRANCH_ANY_RETURN = 1U << 5, /* any return branch */ PERF_SAMPLE_BRANCH_IND_CALL = 1U << 6, /* indirect calls */ PERF_SAMPLE_BRANCH_MAX = 1U << 7, /* non-ABI */ }; #define PERF_SAMPLE_BRANCH_PLM_ALL \ (PERF_SAMPLE_BRANCH_USER|\ PERF_SAMPLE_BRANCH_KERNEL|\ PERF_SAMPLE_BRANCH_HV) /* * Values to determine ABI of the registers dump. */ enum perf_sample_regs_abi { PERF_SAMPLE_REGS_ABI_NONE = 0, PERF_SAMPLE_REGS_ABI_32 = 1, PERF_SAMPLE_REGS_ABI_64 = 2, }; /* * The format of the data returned by read() on a perf event fd, * as specified by attr.read_format: * * struct read_format { * { u64 value; * { u64 time_enabled; } && PERF_FORMAT_TOTAL_TIME_ENABLED * { u64 time_running; } && PERF_FORMAT_TOTAL_TIME_RUNNING * { u64 id; } && PERF_FORMAT_ID * } && !PERF_FORMAT_GROUP * * { u64 nr; * { u64 time_enabled; } && PERF_FORMAT_TOTAL_TIME_ENABLED * { u64 time_running; } && PERF_FORMAT_TOTAL_TIME_RUNNING * { u64 value; * { u64 id; } && PERF_FORMAT_ID * } cntr[nr]; * } && PERF_FORMAT_GROUP * }; */ enum perf_event_read_format { PERF_FORMAT_TOTAL_TIME_ENABLED = 1U << 0, PERF_FORMAT_TOTAL_TIME_RUNNING = 1U << 1, PERF_FORMAT_ID = 1U << 2, PERF_FORMAT_GROUP = 1U << 3, PERF_FORMAT_MAX = 1U << 4, /* non-ABI */ }; #define PERF_ATTR_SIZE_VER0 64 /* sizeof first published struct */ #define PERF_ATTR_SIZE_VER1 72 /* add: config2 */ #define PERF_ATTR_SIZE_VER2 80 /* add: branch_sample_type */ #define PERF_ATTR_SIZE_VER3 96 /* add: sample_regs_user */ /* add: sample_stack_user */ /* * Hardware event_id to monitor via a performance monitoring event: */ struct perf_event_attr { /* * Major type: hardware/software/tracepoint/etc. */ __u32 type; /* * Size of the attr structure, for fwd/bwd compat. */ __u32 size; /* * Type specific configuration information. */ __u64 config; union { __u64 sample_period; __u64 sample_freq; }; __u64 sample_type; __u64 read_format; __u64 disabled : 1, /* off by default */ inherit : 1, /* children inherit it */ pinned : 1, /* must always be on PMU */ exclusive : 1, /* only group on PMU */ exclude_user : 1, /* don't count user */ exclude_kernel : 1, /* ditto kernel */ exclude_hv : 1, /* ditto hypervisor */ exclude_idle : 1, /* don't count when idle */ mmap : 1, /* include mmap data */ comm : 1, /* include comm data */ freq : 1, /* use freq, not period */ inherit_stat : 1, /* per task counts */ enable_on_exec : 1, /* next exec enables */ task : 1, /* trace fork/exit */ watermark : 1, /* wakeup_watermark */ /* * precise_ip: * * 0 - SAMPLE_IP can have arbitrary skid * 1 - SAMPLE_IP must have constant skid * 2 - SAMPLE_IP requested to have 0 skid * 3 - SAMPLE_IP must have 0 skid * * See also PERF_RECORD_MISC_EXACT_IP */ precise_ip : 2, /* skid constraint */ mmap_data : 1, /* non-exec mmap data */ sample_id_all : 1, /* sample_type all events */ exclude_host : 1, /* don't count in host */ exclude_guest : 1, /* don't count in guest */ exclude_callchain_kernel : 1, /* exclude kernel callchains */ exclude_callchain_user : 1, /* exclude user callchains */ __reserved_1 : 41; union { __u32 wakeup_events; /* wakeup every n events */ __u32 wakeup_watermark; /* bytes before wakeup */ }; __u32 bp_type; union { __u64 bp_addr; __u64 config1; /* extension of config */ }; union { __u64 bp_len; __u64 config2; /* extension of config1 */ }; __u64 branch_sample_type; /* enum perf_branch_sample_type */ /* * Defines set of user regs to dump on samples. * See asm/perf_regs.h for details. */ __u64 sample_regs_user; /* * Defines size of the user stack to dump on samples. */ __u32 sample_stack_user; /* Align to u64. */ __u32 __reserved_2; }; #define perf_flags(attr) (*(&(attr)->read_format + 1)) /* * Ioctls that can be done on a perf event fd: */ #define PERF_EVENT_IOC_ENABLE _IO ('$', 0) #define PERF_EVENT_IOC_DISABLE _IO ('$', 1) #define PERF_EVENT_IOC_REFRESH _IO ('$', 2) #define PERF_EVENT_IOC_RESET _IO ('$', 3) #define PERF_EVENT_IOC_PERIOD _IOW('$', 4, __u64) #define PERF_EVENT_IOC_SET_OUTPUT _IO ('$', 5) #define PERF_EVENT_IOC_SET_FILTER _IOW('$', 6, char *) enum perf_event_ioc_flags { PERF_IOC_FLAG_GROUP = 1U << 0, }; /* * Structure of the page that can be mapped via mmap */ struct perf_event_mmap_page { __u32 version; /* version number of this structure */ __u32 compat_version; /* lowest version this is compat with */ /* * Bits needed to read the hw events in user-space. * * u32 seq, time_mult, time_shift, idx, width; * u64 count, enabled, running; * u64 cyc, time_offset; * s64 pmc = 0; * * do { * seq = pc->lock; * barrier() * * enabled = pc->time_enabled; * running = pc->time_running; * * if (pc->cap_usr_time && enabled != running) { * cyc = rdtsc(); * time_offset = pc->time_offset; * time_mult = pc->time_mult; * time_shift = pc->time_shift; * } * * idx = pc->index; * count = pc->offset; * if (pc->cap_usr_rdpmc && idx) { * width = pc->pmc_width; * pmc = rdpmc(idx - 1); * } * * barrier(); * } while (pc->lock != seq); * * NOTE: for obvious reason this only works on self-monitoring * processes. */ __u32 lock; /* seqlock for synchronization */ __u32 index; /* hardware event identifier */ __s64 offset; /* add to hardware event value */ __u64 time_enabled; /* time event active */ __u64 time_running; /* time event on cpu */ union { __u64 capabilities; __u64 cap_usr_time : 1, cap_usr_rdpmc : 1, cap_____res : 62; }; /* * If cap_usr_rdpmc this field provides the bit-width of the value * read using the rdpmc() or equivalent instruction. This can be used * to sign extend the result like: * * pmc <<= 64 - width; * pmc >>= 64 - width; // signed shift right * count += pmc; */ __u16 pmc_width; /* * If cap_usr_time the below fields can be used to compute the time * delta since time_enabled (in ns) using rdtsc or similar. * * u64 quot, rem; * u64 delta; * * quot = (cyc >> time_shift); * rem = cyc & ((1 << time_shift) - 1); * delta = time_offset + quot * time_mult + * ((rem * time_mult) >> time_shift); * * Where time_offset,time_mult,time_shift and cyc are read in the * seqcount loop described above. This delta can then be added to * enabled and possible running (if idx), improving the scaling: * * enabled += delta; * if (idx) * running += delta; * * quot = count / running; * rem = count % running; * count = quot * enabled + (rem * enabled) / running; */ __u16 time_shift; __u32 time_mult; __u64 time_offset; /* * Hole for extension of the self monitor capabilities */ __u64 __reserved[120]; /* align to 1k */ /* * Control data for the mmap() data buffer. * * User-space reading the @data_head value should issue an rmb(), on * SMP capable platforms, after reading this value -- see * perf_event_wakeup(). * * When the mapping is PROT_WRITE the @data_tail value should be * written by userspace to reflect the last read data. In this case * the kernel will not over-write unread data. */ __u64 data_head; /* head in the data section */ __u64 data_tail; /* user-space written tail */ }; #define PERF_RECORD_MISC_CPUMODE_MASK (7 << 0) #define PERF_RECORD_MISC_CPUMODE_UNKNOWN (0 << 0) #define PERF_RECORD_MISC_KERNEL (1 << 0) #define PERF_RECORD_MISC_USER (2 << 0) #define PERF_RECORD_MISC_HYPERVISOR (3 << 0) #define PERF_RECORD_MISC_GUEST_KERNEL (4 << 0) #define PERF_RECORD_MISC_GUEST_USER (5 << 0) #define PERF_RECORD_MISC_MMAP_DATA (1 << 13) /* * Indicates that the content of PERF_SAMPLE_IP points to * the actual instruction that triggered the event. See also * perf_event_attr::precise_ip. */ #define PERF_RECORD_MISC_EXACT_IP (1 << 14) /* * Reserve the last bit to indicate some extended misc field */ #define PERF_RECORD_MISC_EXT_RESERVED (1 << 15) struct perf_event_header { __u32 type; __u16 misc; __u16 size; }; enum perf_event_type { /* * If perf_event_attr.sample_id_all is set then all event types will * have the sample_type selected fields related to where/when * (identity) an event took place (TID, TIME, ID, CPU, STREAM_ID) * described in PERF_RECORD_SAMPLE below, it will be stashed just after * the perf_event_header and the fields already present for the existing * fields, i.e. at the end of the payload. That way a newer perf.data * file will be supported by older perf tools, with these new optional * fields being ignored. * * The MMAP events record the PROT_EXEC mappings so that we can * correlate userspace IPs to code. They have the following structure: * * struct { * struct perf_event_header header; * * u32 pid, tid; * u64 addr; * u64 len; * u64 pgoff; * char filename[]; * }; */ PERF_RECORD_MMAP = 1, /* * struct { * struct perf_event_header header; * u64 id; * u64 lost; * }; */ PERF_RECORD_LOST = 2, /* * struct { * struct perf_event_header header; * * u32 pid, tid; * char comm[]; * }; */ PERF_RECORD_COMM = 3, /* * struct { * struct perf_event_header header; * u32 pid, ppid; * u32 tid, ptid; * u64 time; * }; */ PERF_RECORD_EXIT = 4, /* * struct { * struct perf_event_header header; * u64 time; * u64 id; * u64 stream_id; * }; */ PERF_RECORD_THROTTLE = 5, PERF_RECORD_UNTHROTTLE = 6, /* * struct { * struct perf_event_header header; * u32 pid, ppid; * u32 tid, ptid; * u64 time; * }; */ PERF_RECORD_FORK = 7, /* * struct { * struct perf_event_header header; * u32 pid, tid; * * struct read_format values; * }; */ PERF_RECORD_READ = 8, /* * struct { * struct perf_event_header header; * * { u64 ip; } && PERF_SAMPLE_IP * { u32 pid, tid; } && PERF_SAMPLE_TID * { u64 time; } && PERF_SAMPLE_TIME * { u64 addr; } && PERF_SAMPLE_ADDR * { u64 id; } && PERF_SAMPLE_ID * { u64 stream_id;} && PERF_SAMPLE_STREAM_ID * { u32 cpu, res; } && PERF_SAMPLE_CPU * { u64 period; } && PERF_SAMPLE_PERIOD * * { struct read_format values; } && PERF_SAMPLE_READ * * { u64 nr, * u64 ips[nr]; } && PERF_SAMPLE_CALLCHAIN * * # * # The RAW record below is opaque data wrt the ABI * # * # That is, the ABI doesn't make any promises wrt to * # the stability of its content, it may vary depending * # on event, hardware, kernel version and phase of * # the moon. * # * # In other words, PERF_SAMPLE_RAW contents are not an ABI. * # * * { u32 size; * char data[size];}&& PERF_SAMPLE_RAW * * { u64 from, to, flags } lbr[nr];} && PERF_SAMPLE_BRANCH_STACK * * { u64 abi; # enum perf_sample_regs_abi * u64 regs[weight(mask)]; } && PERF_SAMPLE_REGS_USER * * { u64 size; * char data[size]; * u64 dyn_size; } && PERF_SAMPLE_STACK_USER * * { u64 weight; } && PERF_SAMPLE_WEIGHT * { u64 data_src; } && PERF_SAMPLE_DATA_SRC * }; */ PERF_RECORD_SAMPLE = 9, PERF_RECORD_MAX, /* non-ABI */ }; #define PERF_MAX_STACK_DEPTH 127 enum perf_callchain_context { PERF_CONTEXT_HV = (__u64)-32, PERF_CONTEXT_KERNEL = (__u64)-128, PERF_CONTEXT_USER = (__u64)-512, PERF_CONTEXT_GUEST = (__u64)-2048, PERF_CONTEXT_GUEST_KERNEL = (__u64)-2176, PERF_CONTEXT_GUEST_USER = (__u64)-2560, PERF_CONTEXT_MAX = (__u64)-4095, }; #define PERF_FLAG_FD_NO_GROUP (1U << 0) #define PERF_FLAG_FD_OUTPUT (1U << 1) #define PERF_FLAG_PID_CGROUP (1U << 2) /* pid=cgroup id, per-cpu mode only */ union perf_mem_data_src { __u64 val; struct { __u64 mem_op:5, /* type of opcode */ mem_lvl:14, /* memory hierarchy level */ mem_snoop:5, /* snoop mode */ mem_lock:2, /* lock instr */ mem_dtlb:7, /* tlb access */ mem_rsvd:31; }; }; /* type of opcode (load/store/prefetch,code) */ #define PERF_MEM_OP_NA 0x01 /* not available */ #define PERF_MEM_OP_LOAD 0x02 /* load instruction */ #define PERF_MEM_OP_STORE 0x04 /* store instruction */ #define PERF_MEM_OP_PFETCH 0x08 /* prefetch */ #define PERF_MEM_OP_EXEC 0x10 /* code (execution) */ #define PERF_MEM_OP_SHIFT 0 /* memory hierarchy (memory level, hit or miss) */ #define PERF_MEM_LVL_NA 0x01 /* not available */ #define PERF_MEM_LVL_HIT 0x02 /* hit level */ #define PERF_MEM_LVL_MISS 0x04 /* miss level */ #define PERF_MEM_LVL_L1 0x08 /* L1 */ #define PERF_MEM_LVL_LFB 0x10 /* Line Fill Buffer */ #define PERF_MEM_LVL_L2 0x20 /* L2 hit */ #define PERF_MEM_LVL_L3 0x40 /* L3 hit */ #define PERF_MEM_LVL_LOC_RAM 0x80 /* Local DRAM */ #define PERF_MEM_LVL_REM_RAM1 0x100 /* Remote DRAM (1 hop) */ #define PERF_MEM_LVL_REM_RAM2 0x200 /* Remote DRAM (2 hops) */ #define PERF_MEM_LVL_REM_CCE1 0x400 /* Remote Cache (1 hop) */ #define PERF_MEM_LVL_REM_CCE2 0x800 /* Remote Cache (2 hops) */ #define PERF_MEM_LVL_IO 0x1000 /* I/O memory */ #define PERF_MEM_LVL_UNC 0x2000 /* Uncached memory */ #define PERF_MEM_LVL_SHIFT 5 /* snoop mode */ #define PERF_MEM_SNOOP_NA 0x01 /* not available */ #define PERF_MEM_SNOOP_NONE 0x02 /* no snoop */ #define PERF_MEM_SNOOP_HIT 0x04 /* snoop hit */ #define PERF_MEM_SNOOP_MISS 0x08 /* snoop miss */ #define PERF_MEM_SNOOP_HITM 0x10 /* snoop hit modified */ #define PERF_MEM_SNOOP_SHIFT 19 /* locked instruction */ #define PERF_MEM_LOCK_NA 0x01 /* not available */ #define PERF_MEM_LOCK_LOCKED 0x02 /* locked transaction */ #define PERF_MEM_LOCK_SHIFT 24 /* TLB access */ #define PERF_MEM_TLB_NA 0x01 /* not available */ #define PERF_MEM_TLB_HIT 0x02 /* hit level */ #define PERF_MEM_TLB_MISS 0x04 /* miss level */ #define PERF_MEM_TLB_L1 0x08 /* L1 */ #define PERF_MEM_TLB_L2 0x10 /* L2 */ #define PERF_MEM_TLB_WK 0x20 /* Hardware Walker*/ #define PERF_MEM_TLB_OS 0x40 /* OS fault handler */ #define PERF_MEM_TLB_SHIFT 26 #define PERF_MEM_S(a, s) \ (((u64)PERF_MEM_##a##_##s) << PERF_MEM_##a##_SHIFT) #endif /* _UAPI_LINUX_PERF_EVENT_H */ numatop/common/include/os/os_win.h0000664000175000017500000000563012633407552017125 0ustar jinyaojinyao/* * 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. */ #ifndef _NUMATOP_OS_WIN_H #define _NUMATOP_OS_WIN_H #include #include #include "../types.h" #include "../proc.h" #include "node.h" #include "os_perf.h" #ifdef __cplusplus extern "C" { #endif #define NOTE_LAT \ "Q: Quit; H: Home; B: Back; R: Refresh; " \ "C: Call-Chain; D: Distribution" #define NOTE_LATNODE \ "Q: Quit; H: Home; B: Back; R: Refresh" #define NOTE_LLCALLCHAIN \ "Q: Quit; H: Home; B: Back; R: Refresh" struct _nodeoverview_line; struct _dyn_nodedetail; struct _dyn_callchain; struct _dyn_win; struct _page; struct _lat_line; extern void os_nodeoverview_caption_build(char *, int); extern void os_nodeoverview_data_build(char *, int, struct _nodeoverview_line *, node_t *); extern void os_nodedetail_data(struct _dyn_nodedetail *, win_reg_t *); extern int os_callchain_list_show(struct _dyn_callchain *, track_proc_t *, track_lwp_t *); extern void os_lat_buf_hit(struct _lat_line *, int, os_perf_llrec_t *, uint64_t *, uint64_t *); extern boolean_t os_lat_win_draw(struct _dyn_win *); extern void* os_llcallchain_dyn_create(struct _page *); extern void os_llcallchain_win_destroy(struct _dyn_win *); extern boolean_t os_llcallchain_win_draw(struct _dyn_win *); extern void os_llcallchain_win_scroll(struct _dyn_win *, int); extern boolean_t os_latnode_win_draw(struct _dyn_win *); #ifdef __cplusplus } #endif #endif /* _NUMATOP_OS_WIN_H */ numatop/common/include/os/pfwrapper.h0000664000175000017500000000766312633407552017645 0ustar jinyaojinyao/* * 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. */ #ifndef _NUMATOP_PFWRAPPER_H #define _NUMATOP_PFWRAPPER_H #include #include #include "linux/perf_event.h" #include "../types.h" #ifdef __cplusplus extern "C" { #endif #define PF_MAP_NPAGES_MAX 1024 #define PF_MAP_NPAGES_MIN 64 #define PF_MAP_NPAGES_NORMAL 256 #if defined(__i386__) #ifndef __NR_perf_event_open #define __NR_perf_event_open 336 #endif #define rmb() asm volatile("lock; addl $0,0(%%esp)" ::: "memory") #define wmb() asm volatile("lock; addl $0,0(%%esp)" ::: "memory") #define mb() asm volatile("lock; addl $0,0(%%esp)" ::: "memory") #endif #if defined(__x86_64__) #ifndef __NR_perf_event_open #define __NR_perf_event_open 298 #endif #define rmb() asm volatile("lfence" ::: "memory") #define wmb() asm volatile("sfence" ::: "memory") #define mb() asm volatile("mfence":::"memory") #endif typedef struct _pf_conf { count_id_t count_id; uint32_t type; uint64_t config; uint64_t config1; uint64_t sample_period; } pf_conf_t; typedef struct _pf_profiling_rec { unsigned int pid; unsigned int tid; uint64_t period; count_value_t countval; unsigned int ip_num; uint64_t ips[IP_NUM]; } pf_profiling_rec_t; typedef struct _pf_profiling_rbrec { uint32_t pid; uint32_t tid; uint64_t time_enabled; uint64_t time_running; uint64_t counts[COUNT_NUM]; uint64_t ip_num; } pf_profiling_rbrec_t; typedef struct _pf_ll_rec { unsigned int pid; unsigned int tid; uint64_t addr; uint64_t cpu; uint64_t latency; unsigned int ip_num; uint64_t ips[IP_NUM]; } pf_ll_rec_t; typedef struct _pf_ll_rbrec { unsigned int pid; unsigned int tid; uint64_t addr; uint64_t cpu; uint64_t latency; unsigned int ip_num; } pf_ll_rbrec_t; extern precise_type_t g_precise; struct _perf_cpu; typedef int (*pfn_pf_event_op_t)(struct _perf_cpu *); int pf_ringsize_init(void); int pf_profiling_setup(struct _perf_cpu *, int, pf_conf_t *); int pf_profiling_start(struct _perf_cpu *, count_id_t); int pf_profiling_stop(struct _perf_cpu *, count_id_t); int pf_profiling_allstart(struct _perf_cpu *); int pf_profiling_allstop(struct _perf_cpu *); void pf_profiling_record(struct _perf_cpu *, pf_profiling_rec_t *, int *); int pf_ll_setup(struct _perf_cpu *, pf_conf_t *); int pf_ll_start(struct _perf_cpu *); int pf_ll_stop(struct _perf_cpu *); void pf_ll_record(struct _perf_cpu *, pf_ll_rec_t *, int *); void pf_resource_free(struct _perf_cpu *); #ifdef __cplusplus } #endif #endif /* _NUMATOP_PFWRAPPER_H */ numatop/common/include/os/os_types.h0000664000175000017500000000355212633407552017475 0ustar jinyaojinyao/* * 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. */ #ifndef _NUMATOP_OS_TYPES_H #define _NUMATOP_OS_TYPES_H #ifdef __cplusplus extern "C" { #endif typedef enum { B_FALSE = 0, B_TRUE } boolean_t; #define IP_NUM 32 #define KERNEL_ADDR_START 0xffffffff80000000 #define INVALID_FD -1 #define INVALID_CONFIG (uint64_t)(-1) #ifdef __cplusplus } #endif #endif /* _NUMATOP_OS_TYPES_H */ numatop/common/include/os/map.h0000664000175000017500000000633712633407552016411 0ustar jinyaojinyao/* * 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. */ #ifndef _NUMATOP_MAP_H #define _NUMATOP_MAP_H #include #include #include "../types.h" #include "sym.h" #ifdef __cplusplus extern "C" { #endif #define MAP_R(attr) \ (((attr) >> 3) & 0x1) #define MAP_W(attr) \ (((attr) >> 2) & 0x1) #define MAP_X(attr) \ (((attr) >> 1) & 0x1) #define MAP_P(attr) \ (((attr) & 0x1) == 0) #define MAP_S(attr) \ (((attr) & 0x1) == 1) #define MAP_R_SET(attr) \ ((attr) |= (1 << 3)) #define MAP_W_SET(attr) \ ((attr) |= (1 << 2)) #define MAP_X_SET(attr) \ ((attr) |= (1 << 1)) #define MAP_P_SET(attr) \ ((attr) &= (unsigned int)(~1)) #define MAP_S_SET(attr) \ ((attr) |= 1) #define MAPFILE_LINE_SIZE (PATH_MAX + 1024) #define MAP_ENTRY_NUM 64 typedef struct _numa_entry { uint64_t start_addr; uint64_t end_addr; int nid; } numa_entry_t; typedef struct _numa_map { numa_entry_t *arr; int nentry_cur; int nentry_max; } numa_map_t; typedef struct _map_entry { uint64_t start_addr; uint64_t end_addr; unsigned int attr; boolean_t need_resolve; numa_map_t numa_map; char desc[PATH_MAX]; } map_entry_t; typedef struct _map_proc { map_entry_t *arr; int nentry_cur; int nentry_max; boolean_t loaded; } map_proc_t; typedef struct _map_nodedst { int naccess; unsigned int total_lat; } map_nodedst_t; #define NUMA_MOVE_NPAGES 1024 struct _track_proc; int map_init(void); void map_fini(void); int map_proc_load(struct _track_proc *); int map_proc_fini(struct _track_proc *); map_entry_t* map_entry_find(struct _track_proc *, uint64_t, uint64_t); int map_map2numa(struct _track_proc *, map_entry_t *); int map_addr2nodedst(pid_t pid, void **, int *, int, map_nodedst_t *, int, int *); #ifdef __cplusplus } #endif #endif /* _NUMATOP_SYM_H */ numatop/common/include/util.h0000664000175000017500000000701212633407552016157 0ustar jinyaojinyao/* * 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. */ #ifndef _NUMATOP_UTIL_H #define _NUMATOP_UTIL_H #include #include #include #include #include #include #include "types.h" #include "./os/map.h" #ifdef __cplusplus extern "C" { #endif #define EXIT_MSG_SIZE 128 #define LINE_SIZE 512 #define PROCFS_ID_NUM 4096 #ifndef MIN #define MIN(a, b) ((a) < (b) ? (a) : (b)) #endif #define ASSERT(expr) assert(expr) #define DUMP_CACHE_SIZE 256*1024 #define LOGFILE_PATH "/tmp/numatop.log" typedef struct _cpuid_regs { uint32_t r_eax; uint32_t r_ebx; uint32_t r_ecx; uint32_t r_edx; } cpuid_regs_t; #define CPU_FAMILY(eax) \ (((eax) & 0x0F00) >> 8) #define CPU_MODEL(eax) \ (((eax) & 0x00F0) >> 4) #define CPU_EXT_MODEL(eax) \ (((eax) & 0xF0000) >> 16) typedef struct _debug_ctl { pthread_mutex_t mutex; boolean_t inited; } debug_ctl_t; typedef struct _dump_ctl { FILE *fout; char *cache; char *pcur; int rest_size; boolean_t cache_mode; } dump_ctl_t; extern pid_t g_numatop_pid; extern struct timeval g_tvbase; extern int g_ncpus; extern int g_pagesize; extern double g_nsofclk; extern void *zalloc(size_t n); extern int debug_init(int, FILE *); extern void debug_fini(void); extern void debug_print(FILE *out, int level, const char *fmt, ...); extern uint64_t current_ms(void); extern double ratio(uint64_t value1, uint64_t value2); extern int procfs_enum_id(char *, int **, int *); extern int procfs_proc_enum(pid_t **, int *); extern void exit_msg_put(const char *fmt, ...); extern void exit_msg_print(void); extern uint64_t cyc2ns(uint64_t); extern cpu_type_t cpu_type_get(void); extern int dump_init(FILE *); extern void dump_fini(void); extern void dump_write(const char *fmt, ...); extern void dump_cache_enable(void); extern void dump_cache_disable(void); extern void dump_cache_flush(void); extern void stderr_print(char *format, ...); extern int array_alloc(void **, int *, int *, int, int); extern void pagesize_init(void); #ifdef __cplusplus } #endif #endif /* _NUMATOP_UTIL_H */ numatop/common/include/lwp.h0000664000175000017500000000527012633407552016010 0ustar jinyaojinyao/* * 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. */ #ifndef _NUMATOP_LWP_H #define _NUMATOP_LWP_H #include #include #include #include #include "types.h" #include "perf.h" #include "./os/node.h" #ifdef __cplusplus extern "C" { #endif struct _track_proc; typedef struct _track_lwp { pthread_mutex_t mutex; int ref_count; int id; int intval_ms; int cpuid_max; uint64_t key; boolean_t removing; boolean_t quitting; boolean_t inited; struct _track_proc *proc; count_value_t *countval_arr; perf_countchain_t count_chain; perf_llrecgrp_t llrec_grp; void *perf_priv; } track_lwp_t; extern int lwp_free(track_lwp_t *); extern track_lwp_t *lwp_sort_next(struct _track_proc *); extern void lwp_enum_update(struct _track_proc *); extern int lwp_refcount_inc(track_lwp_t *); extern void lwp_refcount_dec(track_lwp_t *); extern int lwp_key_compute(track_lwp_t *, void *, boolean_t *end); extern int lwp_countval_update(track_lwp_t *, int, count_id_t, uint64_t); extern int lwp_intval_get(track_lwp_t *); extern void lwp_intval_update(struct _track_proc *, int intval_ms); extern void lwp_quitting_set(track_lwp_t *); #ifdef __cplusplus } #endif #endif /* _NUMATOP_LWP_H */ numatop/common/include/perf.h0000664000175000017500000001205212633407552016136 0ustar jinyaojinyao/* * 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. */ #ifndef _NUMATOP_PERF_H #define _NUMATOP_PERF_H #include #include #include #include "types.h" #include "./os/os_perf.h" #ifdef __cplusplus extern "C" { #endif #define PERF_WAIT_NSEC 60 #define PERF_INTVAL_MIN_MS 1000 typedef enum { PERF_STATUS_IDLE = 0, PERF_STATUS_PROFILING_STARTED, PERF_STATUS_PROFILING_PART_STARTED, PERF_STATUS_PROFILING_FAILED, PERF_STATUS_CALLCHAIN_STARTED, PERF_STATUS_CALLCHAIN_FAILED, PERF_STATUS_LL_STARTED, PERF_STATUS_LL_FAILED } perf_status_t; typedef enum { PERF_INVALID_ID = 0, PERF_PROFILING_START_ID, PERF_PROFILING_PARTPAUSE_ID, PERF_PROFILING_RESTORE_ID, PERF_PROFILING_SMPL_ID, PERF_CALLCHAIN_START_ID, PERF_CALLCHAIN_SMPL_ID, PERF_LL_START_ID, PERF_LL_SMPL_ID, PERF_STOP_ID, PERF_QUIT_ID } perf_taskid_t; typedef struct _task_quit { perf_taskid_t task_id; } task_quit_t; typedef struct _task_allstop { perf_taskid_t task_id; } task_allstop_t; typedef struct _task_profiling { perf_taskid_t task_id; } task_profiling_t; typedef struct _task_partpause { perf_taskid_t task_id; count_id_t count_id; } task_partpause_t; typedef struct _task_restore { perf_taskid_t task_id; count_id_t count_id; } task_restore_t; typedef struct _task_callchain { perf_taskid_t task_id; pid_t pid; int lwpid; } task_callchain_t; typedef struct _task_ll { perf_taskid_t task_id; pid_t pid; int lwpid; } task_ll_t; typedef union _perf_task { task_quit_t quit; task_allstop_t allstop; task_profiling_t profiling; task_partpause_t partpause; task_restore_t restore; task_callchain_t callchain; task_ll_t ll; } perf_task_t; typedef struct _perf_llrecgrp { os_perf_llrec_t *rec_arr; int nrec_cur; int nrec_max; int cursor; } perf_llrecgrp_t; typedef struct _perf_chainrec { uint64_t count_value; os_perf_callchain_t callchain; } perf_chainrec_t; typedef struct _perf_chainrecgrp { perf_chainrec_t *rec_arr; int nrec_cur; int nrec_max; int cursor; } perf_chainrecgrp_t; typedef struct _perf_countchain { perf_chainrecgrp_t chaingrps[COUNT_NUM]; } perf_countchain_t; #define TASKID(task_addr) \ (*(perf_taskid_t *)(task_addr)) #define TASKID_SET(task_addr, task_id) \ ((*(perf_taskid_t *)(task_addr)) = (task_id)) #define PERF_PROFILING_STARTED \ (s_perf_ctl.status == PERF_STATUS_PROFILING_STARTED) typedef struct _perf_ctl { pthread_mutex_t mutex; pthread_cond_t cond; pthread_mutex_t status_mutex; pthread_cond_t status_cond; perf_status_t status; pthread_t thr; perf_task_t task; boolean_t inited; uint64_t last_ms; } perf_ctl_t; extern precise_type_t g_precise; extern int perf_init(void); extern void perf_fini(void); extern int perf_allstop(void); extern boolean_t perf_profiling_started(void); extern int perf_profiling_start(void); extern int perf_profiling_smpl(void); extern int perf_profiling_partpause(count_id_t); extern int perf_profiling_restore(count_id_t); extern boolean_t perf_callchain_started(void); extern int perf_callchain_start(pid_t, int); extern int perf_callchain_smpl(void); extern boolean_t perf_ll_started(void); extern int perf_ll_start(pid_t); extern int perf_ll_smpl(pid_t, int); extern void perf_llrecgrp_reset(perf_llrecgrp_t *); extern void perf_countchain_reset(perf_countchain_t *); extern void perf_status_set(perf_status_t); extern void* perf_priv_alloc(boolean_t *); extern void perf_priv_free(void *); extern void perf_task_set(perf_task_t *); extern int perf_status_wait(perf_status_t); extern void perf_smpl_wait(void); extern void perf_ll_started_set(void); #ifdef __cplusplus } #endif #endif /* _NUMATOP_PERF_H */ numatop/common/include/proc.h0000664000175000017500000000751512633407552016155 0ustar jinyaojinyao/* * 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. */ #ifndef _NUMATOP_PROC_H #define _NUMATOP_PROC_H #include #include #include #include #include "types.h" #include "lwp.h" #include "perf.h" #include "./os/node.h" #include "./os/map.h" #include "./os/sym.h" #ifdef __cplusplus extern "C" { #endif #define PROC_NAME_SIZE 16 #define PROC_HASHTBL_SIZE 128 #define PROC_HASHTBL_INDEX(pid) \ ((int)(pid) % PROC_HASHTBL_SIZE) typedef struct _proc_lwplist { int nlwps; int sort_idx; track_lwp_t **id_arr; track_lwp_t **sort_arr; } proc_lwplist_t; typedef struct _track_proc { pthread_mutex_t mutex; int ref_count; boolean_t inited; boolean_t tagged; boolean_t removing; pid_t pid; int flag; int idarr_idx; int cpuid_max; char name[PROC_NAME_SIZE]; proc_lwplist_t lwp_list; int intval_ms; uint64_t key; map_proc_t map; sym_t sym; count_value_t *countval_arr; perf_countchain_t count_chain; perf_llrecgrp_t llrec_grp; struct _track_proc *hash_prev; struct _track_proc *hash_next; struct _track_proc *sort_prev; struct _track_proc *sort_next; } track_proc_t; typedef struct _proc_group { pthread_mutex_t mutex; pthread_cond_t cond; int nprocs; int nlwps; int sort_idx; boolean_t inited; track_proc_t *hashtbl[PROC_HASHTBL_SIZE]; track_proc_t *latest; track_proc_t **sort_arr; } proc_group_t; extern int proc_group_init(void); extern void proc_group_fini(void); extern track_proc_t *proc_find(pid_t); extern track_lwp_t *proc_lwp_find(track_proc_t *, id_t); extern void proc_lwp_count(int *, int *); extern void proc_lwp_resort(track_proc_t *, sort_key_t); extern void proc_group_lock(void); extern void proc_group_unlock(void); extern void proc_resort(sort_key_t); extern track_proc_t *proc_sort_next(void); extern int proc_nlwp(track_proc_t *); extern void proc_enum_update(pid_t); extern int proc_refcount_inc(track_proc_t *); extern void proc_refcount_dec(track_proc_t *); extern void proc_lwp_traverse(track_proc_t *, int (*func)(track_lwp_t *, void *, boolean_t *), void *); extern int proc_countval_update(track_proc_t *, int, count_id_t, uint64_t); extern void proc_intval_update(int); extern int proc_intval_get(track_proc_t *); extern void proc_profiling_clear(void); extern void proc_callchain_clear(void); extern void proc_ll_clear(track_proc_t *); #ifdef __cplusplus } #endif #endif /* _NUMATOP_PROC_H */ numatop/common/include/reg.h0000664000175000017500000000612312633407552015761 0ustar jinyaojinyao/* * 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. */ #ifndef _NUMATOP_REG_H #define _NUMATOP_REG_H #include #include #include #include "types.h" #ifdef __cplusplus extern "C" { #endif typedef enum { ALIGN_LEFT = 0, ALIGN_MIDDLE } reg_align_t; typedef enum { SCROLL_DOWN = 0, SCROLL_UP } reg_scroll_t; typedef struct _scroll_line { boolean_t enabled; /* indicate if support "scrolling" */ int highlight; /* current highlight line. */ int page_start; } scroll_line_t; typedef struct _win_reg { void *hdl; int begin_x; /* offset to stdscr */ int begin_y; /* offset to stdscr */ int ncols_scr; int nlines_scr; unsigned int mode; int nlines_total; void *buf; void (*line_get)(struct _win_reg *, int, char *, int); scroll_line_t scroll; } win_reg_t; extern int reg_init(win_reg_t *, int, int, int, int, unsigned int); extern void reg_buf_init(win_reg_t *, void *, void (*line_get)(win_reg_t *, int, char *, int)); extern void reg_scroll_init(win_reg_t *, boolean_t); extern void reg_erase(win_reg_t *); extern void reg_refresh(win_reg_t *); extern void reg_refresh_nout(win_reg_t *); extern void reg_update_all(void); extern void reg_win_destroy(win_reg_t *seg); extern void reg_line_write(win_reg_t *, int, reg_align_t, char *); extern void reg_highlight_write(win_reg_t *, int, int, char *); extern void reg_line_scroll(win_reg_t *, int); extern void reg_scroll_show(win_reg_t *, void *, int, void (*str_build_func)(char *, int, int, void *)); extern boolean_t reg_curses_init(boolean_t); extern void reg_curses_fini(void); #ifdef __cplusplus } #endif #endif /* _NUMATOP_REG_H */ numatop/common/include/page.h0000664000175000017500000000501012633407552016112 0ustar jinyaojinyao/* * 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. */ #ifndef _NUMATOP_PAGE_H #define _NUMATOP_PAGE_H #include #include "types.h" #include "util.h" #include "win.h" #include "cmd.h" #ifdef __cplusplus extern "C" { #endif #define PAGE_WIN_TYPE(page) \ ((page)->dyn_win.type) #define PAGE_CMD(page) \ (&((page)->cmd)) typedef struct _page { cmd_t cmd; struct _page *prev; struct _page *next; dyn_win_t dyn_win; } page_t; typedef struct _page_list { page_t *head; page_t *tail; page_t *cur; page_t *next_run; int npages; } page_list_t; extern int g_scr_height; extern int g_scr_width; extern void page_list_init(void); extern void page_list_fini(void); extern page_t *page_create(cmd_t *); extern boolean_t page_next_execute(boolean_t); extern page_t *page_current_get(void); extern void page_next_set(page_t *); extern page_t *page_current_set(page_t *); extern void page_drop_next(page_t *); extern page_t *page_curprev_get(void); extern void page_win_destroy(void); #ifdef __cplusplus } #endif #endif /* _NUMATOP_PAGE_H */ numatop/common/include/cmd.h0000664000175000017500000001143312633407552015747 0ustar jinyaojinyao/* * 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. */ #ifndef _NUMATOP_CMD_H #define _NUMATOP_CMD_H #include #include #include "types.h" #include "win.h" #ifdef __cplusplus extern "C" { #endif #define CMD_HOME_CHAR 'h' #define CMD_REFRESH_CHAR 'r' #define CMD_QUIT_CHAR 'q' #define CMD_BACK_CHAR 'b' #define CMD_LATENCY_CHAR 'l' #define CMD_IR_NORMALIZE_CHAR 'i' #define CMD_NODE_OVERVIEW_CHAR 'n' #define CMD_1_CHAR '1' #define CMD_2_CHAR '2' #define CMD_3_CHAR '3' #define CMD_4_CHAR '4' #define CMD_5_CHAR '5' #define CMD_CALLCHAIN_CHAR 'c' #define CMD_ACCDST_CHAR 'd' #define CMD_MAP_GET_CHAR 'm' #define CMD_MAP_STOP_CHAR 's' typedef enum { CMD_INVALID_ID = 0, CMD_HOME_ID, CMD_IR_NORMALIZE_ID, CMD_MONITOR_ID, CMD_LWP_ID, CMD_LAT_ID, CMD_LATNODE_ID, CMD_NODE_OVERVIEW_ID, CMD_NODE_DETAIL_ID, CMD_CALLCHAIN_ID, CMD_LLCALLCHAIN_ID, CMD_ACCDST_ID, CMD_MAP_GET_ID, CMD_MAP_STOP_ID, CMD_1_ID, CMD_2_ID, CMD_3_ID, CMD_4_ID, CMD_5_ID, CMD_REFRESH_ID, CMD_QUIT_ID, CMD_BACK_ID, CMD_RESIZE_ID } cmd_id_t; #define CMD_NUM 23 typedef struct _cmd_home { cmd_id_t id; } cmd_home_t; typedef struct _cmd_ir_normalize { cmd_id_t id; } cmd_ir_normalize_t; typedef struct _cmd_monitor { cmd_id_t id; pid_t pid; int lwpid; } cmd_monitor_t; typedef struct _cmd_lwp { cmd_id_t id; pid_t pid; } cmd_lwp_t; typedef struct _cmd_lat { cmd_id_t id; pid_t pid; int lwpid; } cmd_lat_t; typedef struct _cmd_latnode { cmd_id_t id; pid_t pid; int lwpid; uint64_t addr; uint64_t size; } cmd_latnode_t; typedef struct _cmd_accdst { cmd_id_t id; pid_t pid; int lwpid; } cmd_accdst_t; typedef struct _cmd_node_overview { cmd_id_t id; } cmd_node_overview_t; typedef struct _cmd_node_detail { cmd_id_t id; int nid; } cmd_node_detail_t; typedef struct _cmd_callchain { cmd_id_t id; pid_t pid; int lwpid; } cmd_callchain_t; typedef struct _cmd_llcallchain { cmd_id_t id; pid_t pid; int lwpid; uint64_t addr; uint64_t size; char desc[WIN_DESCBUF_SIZE]; } cmd_llcallchain_t; typedef union _cmd { cmd_home_t home; cmd_ir_normalize_t ir_normalize; cmd_monitor_t monitor; cmd_lwp_t lwp; cmd_lat_t lat; cmd_latnode_t latnode; cmd_node_overview_t node_list; cmd_node_detail_t node_detail; cmd_callchain_t callchain; cmd_llcallchain_t llcallchain; cmd_accdst_t accdst; } cmd_t; typedef int (*pfn_switch_preop_t)(cmd_t *, boolean_t *); typedef int (*pfn_switch_op_t)(cmd_t *, boolean_t); typedef struct _switch { pfn_switch_preop_t preop; pfn_switch_op_t op; } switch_t; #define CMD_ID_SET(cmd_addr, id) \ (*(int *)(cmd_addr) = (id)) #define CMD_ID(cmd_addr) \ (*(int *)(cmd_addr)) #define CMD_LWP(cmd) \ ((cmd_lwp_t *)(cmd)) #define CMD_LAT(cmd) \ ((cmd_lat_t *)(cmd)) #define CMD_LATNODE(cmd) \ ((cmd_latnode_t *)(cmd)) #define CMD_ACCDST(cmd) \ ((cmd_accdst_t *)(cmd)) #define CMD_MONITOR(cmd) \ ((cmd_monitor_t *)(cmd)) #define CMD_NODE_DETAIL(cmd) \ ((cmd_node_detail_t *)(cmd)) #define CMD_CALLCHAIN(cmd) \ ((cmd_callchain_t *)(cmd)) #define CMD_LLCALLCHAIN(cmd) \ ((cmd_llcallchain_t *)(cmd)) extern int g_sortkey; extern void switch_table_init(void); extern int cmd_id_get(char); extern void cmd_execute(cmd_t *, boolean_t *); extern int op_refresh(cmd_t *, boolean_t); extern int op_page_next(cmd_t *, boolean_t); #ifdef __cplusplus } #endif #endif /* _NUMATOP_CMD_H */ numatop/common/include/disp.h0000664000175000017500000000615512633407552016150 0ustar jinyaojinyao/* * 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. */ #ifndef _NUMATOP_DISP_H #define _NUMATOP_DISP_H #include #include #include #include #include "types.h" #include "util.h" #include "cmd.h" #ifdef __cplusplus extern "C" { #endif #define DISP_DEFAULT_INTVAL 5 #define PIPE_CHAR_QUIT 'q' #define PIPE_CHAR_RESIZE 'r' typedef enum { DISP_FLAG_NONE = 0, DISP_FLAG_QUIT, DISP_FLAG_PROFILING_DATA_READY, DISP_FLAG_PROFILING_DATA_FAIL, DISP_FLAG_CALLCHAIN_DATA_READY, DISP_FLAG_CALLCHAIN_DATA_FAIL, DISP_FLAG_LL_DATA_READY, DISP_FLAG_LL_DATA_FAIL, DISP_FLAG_CMD, DISP_FLAG_SCROLLUP, DISP_FLAG_SCROLLDOWN, DISP_FLAG_SCROLLENTER } disp_flag_t; typedef struct _disp_ctl { pthread_mutex_t mutex; pthread_cond_t cond; pthread_t thr; boolean_t inited; cmd_t cmd; disp_flag_t flag; int intval_ms; } disp_ctl_t; typedef struct _cons_ctl { fd_set fds; pthread_t thr; int pipe[2]; boolean_t inited; } cons_ctl_t; extern int g_run_secs; extern int disp_init(void); extern void disp_fini(void); extern int disp_cons_ctl_init(void); extern void disp_cons_ctl_fini(void); extern void disp_consthr_quit(void); extern void disp_profiling_data_ready(int); extern void disp_profiling_data_fail(void); extern void disp_callchain_data_ready(int); extern void disp_callchain_data_fail(void); extern void disp_ll_data_ready(int); extern void disp_ll_data_fail(void); extern void disp_on_resize(int); extern void disp_intval(char *, int); extern void disp_dispthr_quit_wait(void); extern void disp_dispthr_quit_start(void); extern void disp_go_home(void); #ifdef __cplusplus } #endif #endif /* _NUMATOP_DISP_H */ numatop/common/include/win.h0000664000175000017500000002156512633407552016010 0ustar jinyaojinyao/* * 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. */ #ifndef _NUMATOP_WIN_H #define _NUMATOP_WIN_H #include #include #include #include #include "types.h" #include "reg.h" #include "./os/os_win.h" #ifdef __cplusplus extern "C" { #endif #define NUMATOP_TITLE "NumaTOP v1.0, (C) 2013 Intel Corporation" #define CMD_CAPTION "Command: " #define WIN_PROCNAME_SIZE 12 #define WIN_DESCBUF_SIZE 32 #define WIN_CALLCHAIN_NUM 5 #define WIN_LINECHAR_MAX 1024 #define WIN_NLINES_MAX 4096 #define GO_HOME_WAIT 3 #define NOTE_DEFAULT \ "Q: Quit; H: Home; B: Back; R: Refresh; N: Node" #define NOTE_TOPNPROC_RAW \ "Q: Quit; H: Home; R: Refresh; I: IR Normalize; N: Node" #define NOTE_TOPNPROC NOTE_DEFAULT #define NOTE_TOPNLWP NOTE_DEFAULT #define NOTE_MONIPROC \ "Q: Quit; H: Home; B: Back; R: Refresh; " \ "N: Node; L: Latency; C: Call-Chain" #define NOTE_MONILWP NOTE_MONIPROC #define NOTE_NONODE \ "Q: Quit; H: Home; B: Back; R: Refresh" #define NOTE_ACCDST NOTE_NONODE #define NOTE_NODEOVERVIEW NOTE_NONODE #define NOTE_NODEDETAIL NOTE_NONODE #define NOTE_CALLCHAIN NOTE_NONODE #define NOTE_INVALID_PID \ "Invalid process id! (Q: Quit; H: Home)" #define NOTE_INVALID_LWPID \ "Invalid lwp id! (Q: Quit; H: Home)" #define NOTE_INVALID_MAP \ "No memory mapping found! (Q: Quit; H: Home; B: Back)" #define NOTE_INVALID_NUMAMAP \ "No memory NUMA mapping found! (Q: Quit; H: Home; B: Back)" #define CAPTION_PID "PID" #define CAPTION_LWP "LWP" #define CAPTION_RPI "RPI(K)" #define CAPTION_LPI "LPI(K)" #define CAPTION_CPI "CPI" #define CAPTION_CPU "CPU%%" #define CAPTION_NID "NODE" #define CAPTION_PROC "PROC" #define CAPTION_ADDR "ADDR" #define CAPTION_SIZE "SIZE" #define CAPTION_RMA "RMA(K)" #define CAPTION_LMA "LMA(K)" #define CAPTION_RL "RMA/LMA" #define CAPTION_DESC "DESC" #define CAPTION_BUFHIT "ACCESS%%" #define CAPTION_AVGLAT "LAT(ns)" #define CAPTION_MEM_ALL "MEM.ALL" #define CAPTION_MEM_FREE "MEM.FREE" typedef enum { WIN_TYPE_RAW_NUM = 0, WIN_TYPE_TOPNPROC, WIN_TYPE_TOPNLWP, WIN_TYPE_MONILWP, WIN_TYPE_MONIPROC, WIN_TYPE_LAT_PROC, WIN_TYPE_LAT_LWP, WIN_TYPE_LATNODE_PROC, WIN_TYPE_LATNODE_LWP, WIN_TYPE_NODE_OVERVIEW, WIN_TYPE_NODE_DETAIL, WIN_TYPE_CALLCHAIN, WIN_TYPE_LLCALLCHAIN, WIN_TYPE_ACCDST_PROC, WIN_TYPE_ACCDST_LWP } win_type_t; #define WIN_TYPE_NUM 15 typedef enum { WARN_INVALID = 0, WARN_PERF_DATA_FAIL, WARN_INVALID_PID, WARN_INVALID_LWPID, WARN_WAIT, WARN_WAIT_PERF_LL_RESULT, WARN_NOT_IMPL, WARN_GO_HOME, WARN_INVALID_NID, WARN_INVALID_MAP, WARN_INVALID_NUMAMAP, WARN_LL_NOT_SUPPORT, WARN_STOP } warn_type_t; typedef struct _dyn_win { win_type_t type; boolean_t inited; win_reg_t *title; void *dyn; win_reg_t *note; void *page; boolean_t (*draw)(struct _dyn_win *); void (*scroll)(struct _dyn_win *, int); void (*scroll_enter)(struct _dyn_win *); void (*destroy)(struct _dyn_win *); } dyn_win_t; typedef struct _win_countvalue { double rpi; double lpi; double cpu; double cpi; double rma; double lma; double rl; } win_countvalue_t; typedef struct _dyn_topnproc { win_reg_t summary; win_reg_t caption; win_reg_t data; win_reg_t hint; } dyn_topnproc_t; typedef struct _topnproc_line { win_countvalue_t value; char proc_name[WIN_PROCNAME_SIZE]; int pid; int nlwp; } topnproc_line_t; typedef struct _dyn_moniproc { pid_t pid; win_reg_t msg; win_reg_t caption_cur; win_reg_t data_cur; win_reg_t hint; } dyn_moniproc_t; typedef struct _dyn_monilwp { pid_t pid; int lwpid; win_reg_t msg; win_reg_t caption_cur; win_reg_t data_cur; win_reg_t hint; } dyn_monilwp_t; typedef struct _moni_line { win_countvalue_t value; int nid; pid_t pid; } moni_line_t; typedef struct _dyn_topnlwp { pid_t pid; win_reg_t msg; win_reg_t caption; win_reg_t data; win_reg_t hint; } dyn_topnlwp_t; typedef struct _topnlwp_line { win_countvalue_t value; pid_t pid; int lwpid; } topnlwp_line_t; typedef struct _dyn_lat { pid_t pid; int lwpid; win_reg_t msg; win_reg_t caption; win_reg_t data; } dyn_lat_t; typedef struct _dyn_latnode { pid_t pid; int lwpid; uint64_t addr; uint64_t size; win_reg_t msg; win_reg_t note; win_reg_t caption; win_reg_t data; } dyn_latnode_t; typedef struct _lat_line { bufaddr_t bufaddr; /* must be the first field */ int naccess; int latency; int nsamples; pid_t pid; int lwpid; int nid; boolean_t nid_show; char desc[WIN_DESCBUF_SIZE]; } lat_line_t; typedef struct _dyn_accdst { pid_t pid; int lwpid; win_reg_t msg; win_reg_t caption; win_reg_t data; win_reg_t hint; } dyn_accdst_t; typedef struct _accdst_line { int nid; double access_ratio; int latency; } accdst_line_t; typedef struct _dyn_nodeoverview { win_reg_t msg; win_reg_t caption_cur; win_reg_t data_cur; win_reg_t hint; } dyn_nodeoverview_t; typedef struct _nodeoverview_line { win_countvalue_t value; double mem_all; double mem_free; int nid; } nodeoverview_line_t; typedef struct _dyn_nodedetail { int nid; win_reg_t msg; win_reg_t node_data; win_reg_t hint; } dyn_nodedetail_t; typedef struct _dyn_callchain { pid_t pid; int lwpid; count_id_t countid; win_reg_t msg; win_reg_t caption; win_reg_t pad; win_reg_t data; win_reg_t hint; } dyn_callchain_t; typedef struct _callchain_line { char content[WIN_LINECHAR_MAX]; } callchain_line_t; typedef struct _dyn_llcallchain { pid_t pid; int lwpid; uint64_t addr; uint64_t size; char desc[WIN_DESCBUF_SIZE]; win_reg_t msg; win_reg_t buf_caption; win_reg_t buf_data; win_reg_t chain_caption; win_reg_t pad; win_reg_t chain_data; } dyn_llcallchain_t; typedef struct _dyn_warn { win_reg_t msg; win_reg_t pad; } dyn_warn_t; #define DYN_MONI_PROC(page) \ ((dyn_moniproc_t *)((page)->dyn_win.dyn)) #define DYN_MONI_LWP(page) \ ((dyn_monilwp_t *)((page)->dyn_win.dyn)) #define DYN_LAT(page) \ ((dyn_lat_t *)((page)->dyn_win.dyn)) #define DYN_LATNODE(page) \ ((dyn_latnode_t *)((page)->dyn_win.dyn)) #define DYN_ACCDST(page) \ ((dyn_accdst_t *)((page)->dyn_win.dyn)) #define DYN_NODEOVERVIEW(page) \ ((dyn_nodeoverview_t *)((page)->dyn_win.dyn)) #define DYN_NODEDETAIL(page) \ ((dyn_nodedetail_t *)((page)->dyn_win.dyn)) #define DYN_CALLCHAIN(page) \ ((dyn_callchain_t *)((page)->dyn_win.dyn)) #define DYN_LLCALLCHAIN(page) \ ((dyn_llcallchain_t *)((page)->dyn_win.dyn)) /* Screen dimension */ extern int g_scr_height; extern int g_scr_width; /* CPU unhalted cycles in a second */ extern uint64_t g_clkofsec; /* Number of online CPUs */ extern int g_ncpus; /* The sorting key */ extern int g_sortkey; extern void win_fix_init(void); extern void win_fix_fini(void); extern void win_warn_msg(warn_type_t); extern int win_dyn_init(void *); extern void win_dyn_fini(void *); extern void win_node_countvalue(node_t *, win_countvalue_t *); extern void win_callchain_str_build(char *, int, int, void *); extern void win_invalid_proc(void); extern void win_invalid_lwp(void); extern void win_note_show(char *); extern void win_title_show(void); extern boolean_t win_lat_data_show(track_proc_t *, dyn_lat_t *, boolean_t *); extern lat_line_t* win_lat_buf_create(track_proc_t *, int, int *); extern void win_lat_buf_fill(lat_line_t *, int, track_proc_t *, track_lwp_t *, int *); extern int win_lat_cmp(const void *, const void *); extern void win_lat_str_build(char *, int, int, void *); extern void win_size2str(uint64_t, char *, int); extern void win_callchain_line_get(win_reg_t *, int, char *, int); #ifdef __cplusplus } #endif #endif /* _NUMATOP_WIN_H */ numatop/common/win.c0000664000175000017500000021310012633407552014344 0ustar jinyaojinyao/* * 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 #include #include #include #include #include #include #include #include #include #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 cpuid_max, int nid, int ms, int ncpus) { uint64_t rma, lma, ir, clk, all_clks; double d; rma = node_countval_sum(countval_arr, cpuid_max, nid, COUNT_RMA); lma = node_countval_sum(countval_arr, cpuid_max, nid, COUNT_LMA); clk = node_countval_sum(countval_arr, cpuid_max, nid, COUNT_CLK); ir = node_countval_sum(countval_arr, cpuid_max, nid, 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, proc->cpuid_max, 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 v1.0, (C) 2012 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; content = (note != NULL) ? note : NOTE_DEFAULT; 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) { 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) { win_note_show(NOTE_TOPNPROC); } else { win_note_show(NOTE_TOPNPROC_RAW); } reg_update_all(); return (B_TRUE); } /* * The function would be called when user hits the / 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, 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, proc->cpuid_max, 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; win_title_show(); ret = moniproc_data_show(win, ¬e_out); if (!note_out) { win_note_show(NOTE_MONIPROC); } 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, 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, lwp->cpuid_max, 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; win_title_show(); ret = monilwp_data_show(win, ¬e_out); if (!note_out) { win_note_show(NOTE_MONILWP); } 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 / 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 / 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, lwp->cpuid_max, 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, ¬e_out); if (!note_out) { win_note_show(NOTE_TOPNLWP); } reg_update_all(); return (ret); } /* * The function would be called when user hits the / 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, COUNT_RMA); lma = node_countval_get(node, COUNT_LMA); clk = node_countval_get(node, COUNT_CLK); ir = node_countval_get(node, 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, 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, ¬e_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 / 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, ¬e_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->countid = 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(count_id_t count_id, char *buf, int size) { switch (count_id) { case COUNT_RMA: (void) strncpy(buf, "RMA", size); break; case COUNT_CLK: (void) strncpy(buf, "Cycle", size); break; case COUNT_IR: (void) strncpy(buf, "IR", size); break; case 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->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, ¬e_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 / 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 */ 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) { lat_line_t *l1 = (lat_line_t *)p1; lat_line_t *l2 = (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]; *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(); (void) lat_data_get(proc, lwp, dyn, &lat); dump_cache_disable(); disp_intval(intval_buf, 16); if (lwp == NULL) { (void) snprintf(content, sizeof (content), "Monitoring memory areas (pid: %d, " "AVG.LAT: %"PRIu64"ns, interval: %s)", proc->pid, cyc2ns(lat), intval_buf); } else { (void) snprintf(content, sizeof (content), "Monitoring memory areas (lwpid: %d, " "AVG.LAT: %"PRIu64"ns, interval: %s)", lwp->id, cyc2ns(lat), 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 / 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 / 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 / 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, ¬e_out); if (!note_out) { win_note_show(NOTE_ACCDST); } proc_refcount_dec(proc); reg_update_all(); return (ret); } /* * 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; 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; } numatop/common/page.c0000664000175000017500000001245712633407552014477 0ustar jinyaojinyao/* * 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 handle the 'page' which is used in display */ #include #include #include #include #include #include #include #include "include/types.h" #include "include/cmd.h" #include "include/page.h" #include "include/win.h" #include "include/perf.h" #include "include/os/node.h" #include "include/os/os_page.h" static page_list_t s_page_list; /* * Free the resource of a page. */ static void page_free(page_t *page) { if (page != NULL) { win_dyn_fini(page); free(page); } } /* * Initialization for page list. */ void page_list_init(void) { s_page_list.head = NULL; s_page_list.tail = NULL; s_page_list.cur = NULL; s_page_list.next_run = NULL; s_page_list.npages = 0; } /* * Clean up the resources of the page list. */ void page_list_fini(void) { page_t *p1, *p2; p1 = s_page_list.head; while (p1 != NULL) { p2 = p1->next; page_free(p1); p1 = p2; } s_page_list.head = NULL; s_page_list.tail = NULL; s_page_list.cur = NULL; s_page_list.next_run = NULL; s_page_list.npages = 0; } /* * Append a new page to the tail of page list. */ static void page_append(page_t *page) { page_t *tail; page->prev = page->next = NULL; if ((tail = s_page_list.tail) != NULL) { tail->next = page; page->prev = tail; } else { s_page_list.head = page; } s_page_list.tail = page; s_page_list.npages++; } /* * Allocate the resource for the new page. */ page_t * page_create(cmd_t *cmd) { page_t *page; if ((page = zalloc(sizeof (page_t))) == NULL) { return (NULL); } /* * Copy the command information in page. */ (void) memcpy(&page->cmd, cmd, sizeof (cmd_t)); /* * Drop all the pages after the current one. */ page_drop_next(s_page_list.cur); /* * Append the new page after the current one. */ page_append(page); s_page_list.next_run = page; return (page); } /* * Show the page on the screen. */ static boolean_t page_show(page_t *page, boolean_t smpl) { if (g_scr_height < 24 || g_scr_width < 80) { dump_write("\n%s\n", "Terminal size is too small."); dump_write("%s\n", "Please resize it to 80x24 or larger."); return (B_FALSE); } if (node_group_refresh(B_FALSE) != 0) { return (B_FALSE); } if ((!page->dyn_win.inited) && (win_dyn_init(page) != 0)) { return (B_FALSE); } if (smpl) { win_warn_msg(WARN_WAIT); (void) os_page_smpl_start(page); return (B_TRUE); } return (page->dyn_win.draw(&page->dyn_win)); } /* * Show the next page in list. */ boolean_t page_next_execute(boolean_t smpl) { page_t *next_run; boolean_t ret; if ((next_run = s_page_list.next_run) == NULL) { return (B_FALSE); } ret = page_show(next_run, smpl); s_page_list.cur = next_run; if (smpl) { s_page_list.next_run = next_run; } else { s_page_list.next_run = NULL; } return (ret); } page_t * page_current_get(void) { return (s_page_list.cur); } page_t * page_current_set(page_t *page) { s_page_list.cur = page; return (s_page_list.cur); } void page_next_set(page_t *page) { s_page_list.next_run = page; } /* * Free all the pages which are after the specified page node in list. */ void page_drop_next(page_t *page) { page_t *next, *p; if (page == NULL) { return; } next = page->next; while (next != NULL) { p = next->next; page_free(next); s_page_list.npages--; next = p; } page->next = NULL; s_page_list.tail = page; } /* * Get the previous node of current one in list. */ page_t * page_curprev_get(void) { page_t *cur; if ((cur = s_page_list.cur) != NULL) { return (cur->prev); } return (NULL); } /* * Free the resources of windows which are associated with the pages in list. */ void page_win_destroy(void) { page_t *p1; p1 = s_page_list.head; while (p1 != NULL) { win_dyn_fini(p1); p1 = p1->next; } } numatop/kernel_patches/0000775000175000017500000000000012633407552015105 5ustar jinyaojinyaonumatop/kernel_patches/0001-perf-x86-Widen-Haswell-OFFCORE-mask.patch0000664000175000017500000000423412633407552024541 0ustar jinyaojinyaoFrom 1124c7b559c86dd9fadee30e635909f19d6b62e6 Mon Sep 17 00:00:00 2001 From: Andi Kleen Date: Tue, 5 Aug 2014 10:24:18 +0800 Subject: [PATCH] perf, x86: Widen Haswell OFFCORE mask Haswell supports more bits in the offcore_rsp_* MSRs than Sandy Bridge. Previously the Haswell code was using the Sandy Bridge extra register definitions, which prevented users from setting all of these bits. This in term did not allow to set some valid SNOOP_* bits, among others. I allowed all bits the CPU does not #GP on. This is ok because it's protected by a model check. Add a new extra_regs table for Haswell and use. Except for the widened mask it is identical to Sandy Bridge. Signed-off-by: Andi Kleen Signed-off-by: Jin Yao --- arch/x86/kernel/cpu/perf_event_intel.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/arch/x86/kernel/cpu/perf_event_intel.c b/arch/x86/kernel/cpu/perf_event_intel.c index 2502d0d..4f69013 100644 --- a/arch/x86/kernel/cpu/perf_event_intel.c +++ b/arch/x86/kernel/cpu/perf_event_intel.c @@ -189,6 +189,14 @@ static struct extra_reg intel_snbep_extra_regs[] __read_mostly = { EVENT_EXTRA_END }; +static struct extra_reg intel_hsw_extra_regs[] __read_mostly = { + /* must define OFFCORE_RSP_X first, see intel_fixup_er() */ + INTEL_UEVENT_EXTRA_REG(0x01b7, MSR_OFFCORE_RSP_0, 0x3fffff8fffull, RSP_0), + INTEL_UEVENT_EXTRA_REG(0x01bb, MSR_OFFCORE_RSP_1, 0x3fffff8fffull, RSP_1), + INTEL_UEVENT_PEBS_LDLAT_EXTRA_REG(0x01cd), + EVENT_EXTRA_END +}; + EVENT_ATTR_STR(mem-loads, mem_ld_nhm, "event=0x0b,umask=0x10,ldlat=3"); EVENT_ATTR_STR(mem-loads, mem_ld_snb, "event=0xcd,umask=0x1,ldlat=3"); EVENT_ATTR_STR(mem-stores, mem_st_snb, "event=0xcd,umask=0x2"); @@ -2552,7 +2560,7 @@ __init int intel_pmu_init(void) x86_pmu.event_constraints = intel_hsw_event_constraints; x86_pmu.pebs_constraints = intel_hsw_pebs_event_constraints; - x86_pmu.extra_regs = intel_snb_extra_regs; + x86_pmu.extra_regs = intel_hsw_extra_regs; x86_pmu.pebs_aliases = intel_pebs_aliases_snb; /* all extra regs are per-cpu when HT is on */ x86_pmu.er_flags |= ERF_HAS_RSP_1; -- 1.9.1 numatop/Makefile0000644000175000017500000000166412633415311013553 0ustar jinyaojinyaoPREFIXDIR = /usr/local BINDIR = /bin MANDIR = /usr/share/man/man8 PROG = numatop CC = gcc LD = gcc CFLAGS = -g -Wall -O2 LDFLAGS = -g LDLIBS = -lncurses -lpthread -lnuma COMMON_OBJS = cmd.o disp.o lwp.o numatop.o page.o perf.o \ proc.o reg.o util.o win.o OS_OBJS = os_cmd.o os_perf.o os_win.o node.o map.o \ os_util.o plat.o pfwrapper.o sym.o os_page.o INTEL_OBJS = wsm.o snb.o nhm.o bdw.o all: $(PROG) $(PROG): $(COMMON_OBJS) $(OS_OBJS) $(INTEL_OBJS) $(LD) $(LDFLAGS) -o $@ $(COMMON_OBJS) $(OS_OBJS) $(INTEL_OBJS) $(LDLIBS) %.o: ./common/%.c ./common/include/*.h ./common/include/os/*.h $(CC) $(CFLAGS) -o $@ -c $< %.o: ./common/os/%.c ./common/include/*.h ./common/include/os/*.h $(CC) $(CFLAGS) -o $@ -c $< %.o: ./intel/%.c ./intel/include/*.h $(CC) $(CFLAGS) -o $@ -c $< install: $(PROG) install -m 0755 $(PROG) $(PREFIXDIR)$(BINDIR)/ gzip -c numatop.8 > numatop.8.gz mv -f numatop.8.gz $(MANDIR)/ clean: rm -rf *.o $(PROG) numatop/intel/0000775000175000017500000000000012633415043013223 5ustar jinyaojinyaonumatop/intel/snb.c0000664000175000017500000000543312633407552014164 0ustar jinyaojinyao/* * 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 the Sandy-Bridge platform specific functions. */ #include #include #include #include #include #include #include #include "../common/include/types.h" #include "../common/include/os/linux/perf_event.h" #include "../common/include/os/plat.h" #include "../common/include/os/os_perf.h" #include "include/snb.h" static plat_event_config_t s_snb_ep_config[COUNT_NUM] = { { PERF_TYPE_HARDWARE, PERF_COUNT_HW_CPU_CYCLES, 0x53, 0, "cpu_clk_unhalted.core" }, { PERF_TYPE_RAW, 0x01B7, 0x53, 0x67f800001, "off_core_response_0" }, { PERF_TYPE_HARDWARE, PERF_COUNT_HW_REF_CPU_CYCLES, 0x53, 0, "cpu_clk_unhalted.ref" }, { PERF_TYPE_HARDWARE, PERF_COUNT_HW_INSTRUCTIONS, 0x53, 0, "instr_retired.any" }, { PERF_TYPE_RAW, 0x01BB, 0x53, 0x600400001, "off_core_response_1" } }; static plat_event_config_t s_snb_ll = { PERF_TYPE_RAW, 0x01CD, 0x53, LL_THRESH, "mem_trans_retired.latency_above_threshold" }; void snbep_profiling_config(count_id_t count_id, plat_event_config_t *cfg) { plat_config_get(count_id, cfg, s_snb_ep_config); } void snbep_ll_config(plat_event_config_t *cfg) { memcpy(cfg, &s_snb_ll, sizeof (plat_event_config_t)); } int snb_offcore_num(void) { return (2); } numatop/intel/bdw.c0000664000175000017500000000541012633415034014143 0ustar jinyaojinyao/* * 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 the bdw platform specific functions. */ #include #include #include #include #include #include #include #include "../common/include/types.h" #include "../common/include/os/linux/perf_event.h" #include "../common/include/os/plat.h" #include "../common/include/os/os_perf.h" #include "include/bdw.h" static plat_event_config_t s_bdw_config[COUNT_NUM] = { { PERF_TYPE_HARDWARE, PERF_COUNT_HW_CPU_CYCLES, 0x53, 0, "cpu_clk_unhalted.core" }, { PERF_TYPE_RAW, 0x01B7, 0x53, 0x638000001, "off_core_response_0" }, { PERF_TYPE_HARDWARE, PERF_COUNT_HW_REF_CPU_CYCLES, 0x53, 0, "cpu_clk_unhalted.ref" }, { PERF_TYPE_HARDWARE, PERF_COUNT_HW_INSTRUCTIONS, 0x53, 0, "instr_retired.any" }, { PERF_TYPE_RAW, 0x01BB, 0x53, 0x604000001, "off_core_response_1" } }; static plat_event_config_t s_bdw_ll = { PERF_TYPE_RAW, 0x01CD, 0x53, LL_THRESH, "mem_trans_retired.latency_above_threshold" }; void bdw_profiling_config(count_id_t count_id, plat_event_config_t *cfg) { plat_config_get(count_id, cfg, s_bdw_config); } void bdw_ll_config(plat_event_config_t *cfg) { memcpy(cfg, &s_bdw_ll, sizeof (plat_event_config_t)); } int bdw_offcore_num(void) { return (2); } numatop/intel/wsm.c0000664000175000017500000000670412633407552014212 0ustar jinyaojinyao/* * 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 the Westmere platform specific functions. */ #include #include #include #include #include #include #include #include "../common/include/types.h" #include "../common/include/os/linux/perf_event.h" #include "../common/include/os/plat.h" #include "../common/include/os/os_perf.h" #include "include/wsm.h" static plat_event_config_t s_wsmex_profiling[COUNT_NUM] = { { PERF_TYPE_HARDWARE, PERF_COUNT_HW_CPU_CYCLES, 0x53, 0, "cpu_clk_unhalted.core" }, { PERF_TYPE_RAW, 0x01B7, 0x53, 0x2011, "off_core_response_0" }, { PERF_TYPE_HARDWARE, PERF_COUNT_HW_REF_CPU_CYCLES, 0x53, 0, "cpu_clk_unhalted.ref" }, { PERF_TYPE_HARDWARE, PERF_COUNT_HW_INSTRUCTIONS, 0x53, 0, "instr_retired.any" }, { PERF_TYPE_RAW, 0x01BB, 0x53, 0x5011, "off_core_response_1" } }; static plat_event_config_t s_wsmep_profiling[COUNT_NUM] = { { PERF_TYPE_HARDWARE, PERF_COUNT_HW_CPU_CYCLES, 0x53, 0, "cpu_clk_unhalted.core" }, { PERF_TYPE_RAW, 0x01B7, 0x53, 0x2011, "off_core_response_0" }, { PERF_TYPE_HARDWARE, PERF_COUNT_HW_REF_CPU_CYCLES, 0x53, 0, "cpu_clk_unhalted.ref" }, { PERF_TYPE_HARDWARE, PERF_COUNT_HW_INSTRUCTIONS, 0x53, 0, "instr_retired.any" }, { PERF_TYPE_RAW, INVALID_CODE_UMASK, 0, 0, "off_core_response_1" } }; static plat_event_config_t s_wsm_ll = { PERF_TYPE_RAW, 0x100B, 0x53, LL_THRESH, "mem_inst_retired.latency_above_threshold" }; void wsmex_profiling_config(count_id_t count_id, plat_event_config_t *cfg) { plat_config_get(count_id, cfg, s_wsmex_profiling); } void wsmep_profiling_config(count_id_t count_id, plat_event_config_t *cfg) { plat_config_get(count_id, cfg, s_wsmep_profiling); } void wsmex_ll_config(plat_event_config_t *cfg) { memcpy(cfg, &s_wsm_ll, sizeof (plat_event_config_t)); } void wsmep_ll_config(plat_event_config_t *cfg) { memcpy(cfg, &s_wsm_ll, sizeof (plat_event_config_t)); } int wsm_offcore_num(void) { return (2); } numatop/intel/nhm.c0000664000175000017500000000657212633407552014171 0ustar jinyaojinyao/* * 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 the Nehalem platform specific functions. */ #include #include #include #include #include #include #include #include "../common/include/types.h" #include "../common/include/os/linux/perf_event.h" #include "../common/include/os/plat.h" #include "../common/include/os/os_perf.h" #include "include/nhm.h" static plat_event_config_t s_nhm_profiling[COUNT_NUM] = { { PERF_TYPE_HARDWARE, PERF_COUNT_HW_CPU_CYCLES, 0x53, 0, "cpu_clk_unhalted.core" }, { PERF_TYPE_RAW, 0x01B7, 0x53, 0x2011, "off_core_response_0" }, { PERF_TYPE_HARDWARE, PERF_COUNT_HW_REF_CPU_CYCLES, 0x53, 0, "cpu_clk_unhalted.ref" }, { PERF_TYPE_HARDWARE, PERF_COUNT_HW_INSTRUCTIONS, 0x53, 0, "instr_retired.any" }, { PERF_TYPE_RAW, INVALID_CODE_UMASK, 0, 0, "off_core_response_1" } }; static plat_event_config_t s_nhm_ll = { PERF_TYPE_RAW, 0x100B, 0x53, LL_THRESH, "mem_inst_retired.latency_above_threshold" }; static void config_get(count_id_t count_id, plat_event_config_t *cfg, plat_event_config_t *cfg_arr) { cfg->type = cfg_arr[count_id].type; cfg->config = cfg_arr[count_id].config; cfg->other_attr = cfg_arr[count_id].other_attr; cfg->extra_value = cfg_arr[count_id].extra_value; strncpy(cfg->desc, cfg_arr[count_id].desc, PLAT_EVENT_DESC_SIZE); cfg->desc[PLAT_EVENT_DESC_SIZE - 1] = 0; } void nhmex_profiling_config(count_id_t count_id, plat_event_config_t *cfg) { config_get(count_id, cfg, s_nhm_profiling); } void nhmep_profiling_config(count_id_t count_id, plat_event_config_t *cfg) { config_get(count_id, cfg, s_nhm_profiling); } void nhmex_ll_config(plat_event_config_t *cfg) { memcpy(cfg, &s_nhm_ll, sizeof (plat_event_config_t)); } void nhmep_ll_config(plat_event_config_t *cfg) { memcpy(cfg, &s_nhm_ll, sizeof (plat_event_config_t)); } int nhm_offcore_num(void) { return (1); } numatop/intel/include/0000775000175000017500000000000012633415051014645 5ustar jinyaojinyaonumatop/intel/include/bdw.h0000664000175000017500000000370412633415051015576 0ustar jinyaojinyao/* * 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. */ #ifndef _NUMATOP_BDW_H #define _NUMATOP_BDW_H #ifdef __cplusplus extern "C" { #endif #include #include #include "../../common/include/types.h" struct _plat_event_config; extern void bdw_profiling_config(count_id_t, struct _plat_event_config *); extern void bdw_ll_config(struct _plat_event_config *); extern int bdw_offcore_num(void); #ifdef __cplusplus } #endif #endif /* _NUMATOP_BDW_H */ numatop/intel/include/wsm.h0000664000175000017500000000411712633407552015636 0ustar jinyaojinyao/* * 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. */ #ifndef _NUMATOP_WSM_H #define _NUMATOP_WSM_H #ifdef __cplusplus extern "C" { #endif #include #include #include "../../common/include/types.h" struct _plat_event_config; extern void wsmex_profiling_config(count_id_t, struct _plat_event_config *); extern void wsmep_profiling_config(count_id_t, struct _plat_event_config *); extern void wsmex_ll_config(struct _plat_event_config *); extern void wsmep_ll_config(struct _plat_event_config *); extern int wsm_offcore_num(void); #ifdef __cplusplus } #endif #endif /* _NUMATOP_WSM_H */ numatop/intel/include/nhm.h0000664000175000017500000000411712633407552015612 0ustar jinyaojinyao/* * 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. */ #ifndef _NUMATOP_NHM_H #define _NUMATOP_NHM_H #ifdef __cplusplus extern "C" { #endif #include #include #include "../../common/include/types.h" struct _plat_event_config; extern void nhmex_profiling_config(count_id_t, struct _plat_event_config *); extern void nhmep_profiling_config(count_id_t, struct _plat_event_config *); extern void nhmex_ll_config(struct _plat_event_config *); extern void nhmep_ll_config(struct _plat_event_config *); extern int nhm_offcore_num(void); #ifdef __cplusplus } #endif #endif /* _NUMATOP_NHM_H */ numatop/intel/include/snb.h0000664000175000017500000000371012633407552015610 0ustar jinyaojinyao/* * 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. */ #ifndef _NUMATOP_SNB_H #define _NUMATOP_SNB_H #ifdef __cplusplus extern "C" { #endif #include #include #include "../../common/include/types.h" struct _plat_event_config; extern void snbep_profiling_config(count_id_t, struct _plat_event_config *); extern void snbep_ll_config(struct _plat_event_config *); extern int snb_offcore_num(void); #ifdef __cplusplus } #endif #endif /* _NUMATOP_SNB_H */ numatop/numatop.80000664000175000017500000003306212633407552013676 0ustar jinyaojinyao.TH NUMATOP 8 "April 3, 2013" .\" Please adjust this date whenever revising the manpage. .\" .\" Some roff macros, for reference: .\" .nh disable hyphenation .\" .hy enable hyphenation .\" .ad l left justify .\" .ad b justify to both left and right margins .\" .nf disable filling .\" .fi enable filling .\" .br insert line break .\" .sp insert n+1 empty lines .\" for manpage-specific macros, see man(7) .SH NAME numatop \- a tool for memory access locality characterization and analysis. .SH SYNOPSIS .B numatop .RI [ -s ] " " [ -l ] " " [ -f ] " " [ -d ] .PP .B numatop .RI [ -h ] .SH DESCRIPTION This manual page briefly documents the .B numatop command. .PP Most modern systems use a Non-Uniform Memory Access (NUMA) design for multiprocessing. In NUMA systems, memory and processors are organized in such a way that some parts of memory are closer to a given processor, while other parts are farther from it. A processor can access memory that is closer to it much faster than the memory that is farther from it. Hence, the latency between the processors and different portions of the memory in a NUMA machine may be significantly different. \fBnumatop\fP is an observation tool for runtime memory locality characterization and analysis of processes and threads running on a NUMA system. It helps the user to characterize the NUMA behavior of processes and threads and to identify where the NUMA-related performance bottlenecks reside. The tool uses Intel performance counter sampling technologies and associates the performance data with Linux system runtime information to provide real-time analysis in production systems. The tool can be used to: \fBA)\fP Characterize the locality of all running processes and threads to identify those with the poorest locality in the system. \fBB)\fP Identify the "hot" memory areas, report average memory access latency, and provide the location where accessed memory is allocated. A "hot" memory area is where process/thread(s) accesses are most frequent. numatop has a metric called "ACCESS%" that specifies what percentage of memory accesses are attributable to each memory area. \fBNote: numatop records only the memory accesses which have latencies greater than a predefined threshold (128 CPU cycles).\fP \fBC)\fP Provide the call-chain(s) in the process/thread code that accesses a given hot memory area. \fBD)\fP Provide the call-chain(s) when the process/thread generates certain counter events (RMA/LMA/IR/CYCLE). The call-chain(s) helps to locate the source code that generates the events. .PP RMA: Remote Memory Access. .br LMA: Local Memory Access. .br IR: Instruction Retired. .br CYCLE: CPU cycles. .br \fBE)\fP Provide per-node statistics for memory and CPU utilization. A node is: a region of memory in which every byte has the same distance from each CPU. \fBF)\fP Show, using a user-friendly interface, the list of processes/threads sorted by some metrics (by default, sorted by CPU utilization), with the top process having the highest CPU utilization in the system and the bottom one having the lowest CPU utilization. Users can also use hotkeys to resort the output by these metrics: RMA, LMA, RMA/LMA, CPI, and CPU%. .br RMA/LMA: ratio of RMA/LMA. .br CPI: CPU cycle per instruction. .br CPU%: CPU utilization. .br \fBnumatop\fP is a GUI tool that periodically tracks and analyzes the NUMA activity of processes and threads and displays useful metrics. Users can scroll up/down by using the up or down key to navigate in the current window and can use several hot keys shown at the bottom of the window, to switch between windows or to change the running state of the tool. For example, hotkey 'R' refreshes the data in the current window. Below is a detailed description of the various display windows and the data items that they display: \fB[WIN1 - Monitoring processes and threads]:\fP .br Get the locality characterization of all processes. This is the first window upon startup, it's numatop's "Home" window. This window displays a list of processes. The top process has the highest system CPU utilization (CPU%), while the bottom process has the lowest CPU% in the system. Generally, the memory-intensive process is also CPU-intensive, so the processes shown in this window are sorted by CPU% by default. The user can press hotkeys '1', '2', '3', '4', or '5' to resort the output by "RMA", "LMA", "RMA/LMA", "CPI", or "CPU%". .PP \fB[KEY METRICS]:\fP .br RMA(K): number of Remote Memory Access (unit is 1000). .br RMA(K) = RMA / 1000; .br LMA(K): number of Local Memory Access (unit is 1000). .br LMA(K) = LMA / 1000; .br RMA/LMA: ratio of RMA/LMA. .br CPI: CPU cycles per instruction. .br CPU%: system CPU utilization (busy time across all CPUs). .PP \fB[HOTKEY]:\fP .br Q: Quit the application. .br H: WIN1 refresh. .br R: Refresh to show the latest data. .br I: Switch to WIN2 to show the normalized data. .br N: Switch to WIN11 to show the per-node statistics. .br 1: Sort by RMA. .br 2: Sort by LMA. .br 3: Sort by RMA/LMA. .br 4: Sort by CPI. .br 5: Sort by CPU% .PP \fB[WIN2 - Monitoring processes and threads (normalized)]:\fP .br Get the normalized locality characterization of all processes. .PP \fB[KEY METRICS]:\fP .br RPI(K): RMA normalized by 1000 instructions. .br RPI(K) = RMA / (IR / 1000); .br LPI(K): LMA normalized by 1000 instructions. .br LPI(K) = LMA / (IR / 1000); .br Other metrics remain the same. .PP \fB[HOTKEY]:\fP .br Q: Quit the application. .br H: Switch to WIN1. .br B: Back to previous window. .br R: Refresh to show the latest data. .br N: Switch to WIN11 to show the per-node statistics. .br 1: Sort by RPI. .br 2: Sort by LPI. .br 3: Sort by RMA/LMA. .br 4: Sort by CPI. .br 5: Sort by CPU% .PP \fB[WIN3 - Monitoring the process]:\fP .br Get the locality characterization with node affinity of a specified process. .PP \fB[KEY METRICS]:\fP .br NODE: the node ID. .br CPU%: per-node CPU utilization. .br Other metrics remain the same. .PP \fB[HOTKEY]:\fP .br Q: Quit the application. .br H: Switch to WIN1. .br B: Back to previous window. .br R: Refresh to show the latest data. .br N: Switch to WIN11 to show the per-node statistics. .br L: Show the latency information. .br C: Show the call-chain. .PP \fB[WIN4 - Monitoring all threads]:\fP .br Get the locality characterization of all threads in a specified process. .PP \fB[KEY METRICS]\fP: .br CPU%: per-CPU CPU utilization. .br Other metrics remain the same. .PP \fB[HOTKEY]:\fP .br Q: Quit the application. .br H: Switch to WIN1. .br B: Back to previous window. .br R: Refresh to show the latest data. .br N: Switch to WIN11 to show the per-node statistics. .PP \fB[WIN5 - Monitoring the thread]:\fP .br Get the locality characterization with node affinity of a specified thread. .PP \fB[KEY METRICS]:\fP .br CPU%: per-CPU CPU utilization. .br Other metrics remain the same. .PP \fB[HOTKEY]:\fP .br Q: Quit the application. .br H: Switch to WIN1. .br B: Back to previous window. .br R: Refresh to show the latest data. .br N: Switch to WIN11 to show the per-node statistics. .br L: Show the latency information. .br C: Show the call-chain. .PP \fB[WIN6 - Monitoring memory areas]:\fP .br Get the memory area use with the associated accessing latency of a specified process/thread. .PP \fB[KEY METRICS]:\fP .br ADDR: starting address of the memory area. .br SIZE: size of memory area (K/M/G bytes). .br ACCESS%: percentage of memory accesses are to this memory area. .br LAT(ns): the average latency (nanoseconds) of memory accesses. .br DESC: description of memory area (from /proc//maps). .PP \fB[HOTKEY]:\fP .br Q: Quit the application. .br H: Switch to WIN1. .br B: Back to previous window. .br R: Refresh to show the latest data. .br A: Show the memory access node distribution. .br C: Show the call-chain when process/thread accesses the memory area. .PP \fB[WIN7 - Memory access node distribution overview]:\fP .br Get the percentage of memory accesses originated from the process/thread to each node. .PP \fB[KEY METRICS]:\fP .br NODE: the node ID. .br ACCESS%: percentage of memory accesses are to this node. .br LAT(ns): the average latency (nanoseconds) of memory accesses to this node. .PP \fB[HOTKEY]:\fP .br Q: Quit the application. .br H: Switch to WIN1. .br B: Back to previous window. .br R: Refresh to show the latest data. .PP \fB[WIN8 - Break down the memory area into physical memory on node]:\fP .br Break down the memory area into the physical mapping on node with the associated accessing latency of a process/thread. .PP \fB[KEY METRICS]:\fP .br NODE: the node ID. .br Other metrics remain the same. .PP \fB[HOTKEY]:\fP .br Q: Quit the application. .br H: Switch to WIN1. .br B: Back to previous window. .br R: Refresh to show the latest data. .PP \fB[WIN9 - Call-chain when process/thread generates the event ("RMA"/"LMA"/"CYCLE"/"IR")]:\fP .br Determine the call-chains to the code that generates "RMA"/"LMA"/"CYCLE"/"IR". .PP \fB[KEY METRICS]:\fP .br Call-chain list: a list of call-chains. .PP \fB[HOTKEY]:\fP .br Q: Quit the application. .br H: Switch to WIN1. .br B: Back to the previous window. .br R: Refresh to show the latest data. .br 1: Locate call-chain when process/thread generates "RMA" .br 2: Locate call-chain when process/thread generates "LMA" .br 3: Locate call-chain when process/thread generates "CYCLE" (CPU cycle) .br 4: Locate call-chain when process/thread generates "IR" (Instruction Retired) .PP \fB[WIN10 - Call-chain when process/thread access the memory area]:\fP .br Determine the call-chains to the code that references this memory area. The latency must be greater than the predefined latency threshold (128 CPU cycles). .PP \fB[KEY METRICS]:\fP .br Call-chain list: a list of call-chains. .br Other metrics remain the same. .PP \fB[HOTKEY]:\fP .br Q: Quit the application. .br H: Switch to WIN1. .br B: Back to previous window. .br R: Refresh to show the latest data. .PP \fB[WIN11 - Node Overview]:\fP .br Show the basic per-node statistics for this system .PP \fB[KEY METRICS]:\fP .br MEM.ALL: total usable RAM (physical RAM minus a few reserved bits and the kernel binary code). .br MEM.FREE: sum of LowFree + HighFree (overall stat) . .br CPU%: per-node CPU utilization. .br Other metrics remain the same. .PP \fB[WIN12 - Information of Node N]:\fP .br Show the memory use and CPU utilization for the selected node. .PP \fB[KEY METRICS]:\fP .br CPU: array of logical CPUs which belong to this node. .br CPU%: per-node CPU utilization. .br MEM active: the amount of memory that has been used more recently and is not usually reclaimed unless absolute necessary. .br MEM inactive: the amount of memory that has not been used for a while and is eligible to be swapped to disk. .br Dirty: the amount of memory waiting to be written back to the disk. .br Writeback: the amount of memory actively being written back to the disk. .br Mapped: all pages mapped into a process. .PP \fB[HOTKEY]:\fP .br Q: Quit the application. .br H: Switch to WIN1. .br B: Back to previous window. .br R: Refresh to show the latest data. .PP .SH "OPTIONS" The following options are supported by numatop: .PP -s sampling_precision .br normal: balance precision and overhead (default) .br high: high sampling precision (high overhead) .br low: low sampling precision, suitable for high load system .PP -l log_level .br Specifies the level of logging in the log file. Valid values are: .br 1: unknown (reserved for future use) .br 2: all .PP -f log_file .br Specifies the log file where output will be written. If the log file is not writable, the tool will prompt "Cannot open '' for writting.". .PP -d dump_file .br Specifies the dump file where the screen data will be written. Generally the dump file is used for automated test. If the dump file is not writable, the tool will prompt "Cannot open for dump writing." .PP -h .br Displays the command's usage. .PP .SH EXAMPLES Example 1: Launch numatop with high sampling precision .br numatop -s high .PP Example 2: Write all warning messages in /tmp/numatop.log .br numatop -l 2 -o /tmp/numatop.log .PP Example 3: Dump screen data in /tmp/dump.log .br numatop -d /tmp/dump.log .PP .SH EXIT STATUS .br 0: successful operation. .br Other value: an error occurred. .PP .SH USAGE .br You must have root privileges to run numatop. .br Or set -1 in /proc/sys/kernel/perf_event_paranoid .PP \fBNote\fP: The perf_event_paranoid setting has security implications and a non-root user probably doesn't have authority to access /proc. It is highly recommended that the user runs \fBnumatop\fP as root. .PP .SH VERSION .br \fBnumatop\fP requires a patch set to support PEBS Load Latency functionality in the kernel. The patch set has not been integrated in 3.8. Probably it will be integrated in 3.9. The following steps show how to get and apply the patch set. .PP 1. git clone git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip.git .br 2. cd tip .br 3. git checkout perf/x86 .br 4. build kernel as usual .PP \fBnumatop\fP supports the Intel Xeon processors: 5500-series, 6500/7500-series, 5600 series, E7-x8xx-series, and E5-16xx/24xx/26xx/46xx-series. \fBNote\fP: CPU microcode version 0x618 or 0x70c or later is required on E5-16xx/24xx/26xx/46xx-series. numatop/test/0000775000175000017500000000000012633407552013075 5ustar jinyaojinyaonumatop/test/mgen/0000775000175000017500000000000012633407552014023 5ustar jinyaojinyaonumatop/test/mgen/README0000664000175000017500000000110312633407552014676 0ustar jinyaojinyaoBuilding --------- 1. To build mgen make 2. To clean the built objects make clean Note ---- mgen is a micro-test application which can generate memory access with runtime latency between CPUs. Please note the latencies reported by mgen are not the official latencies from Intel. It is just a tool to test numatop. For example: 1. Generate LMA (Local Memory Access) on cpu1 for running 1000s: ./mgen -a 1 -c 1 -t 1000 2. Generate RMA (Remote Memory Access) from cpu10 to cpu0 for running 1000s. (cpu10 and cpu0 are on different nodes): ./mgen -a 0 -c 10 -t 1000 numatop/test/mgen/Makefile0000664000175000017500000000032312633407552015461 0ustar jinyaojinyaoPROG = mgen CC = gcc LD = gcc CFLAGS = -g -Wall LDLIBS = -lpthread OBJS = mgen.o all: $(PROG) $(PROG): $(OBJS) $(LD) $(LDLIBS) -o $@ $(OBJS) %.o: %.c $(CC) $(CFLAGS) -o $@ -c $< clean: rm -rf *.o mgen numatop/test/mgen/mgen.c0000664000175000017500000003370112633407552015121 0ustar jinyaojinyao/* * 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. */ /* * The application can generate LMA (Local Memory Access) and RMA * (Remote Memory Access) with latency information on NUMA system. * * Please note the latencies reported by mgen are not the official data * from Intel. It is just a tool to test numatop. */ #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifndef PATH_MAX #define PATH_MAX 4096 #endif #define CPU0_CPUFREQ_PATH \ "/sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq" #define CPUINFO_PATH \ "/proc/cpuinfo" #define MEAS_TIME_DEFAULT 5 #define MAX_VALUE 4294967295U #define MHZ 1000000 #define GHZ 1000000000 #define KHZ 1000 #define NS_SEC 1000000000 #define NS_MS 1000000 #define MS_SEC 1000 #define MICROSEC 1000000 #define BUF_SIZE 256 * 1024 * 1024 #define RAND_ARRAY_SIZE 8192 #define INVALID_RAND -1 #define LINE_SIZE 128 #define READ_NUM 10240000 static double s_nsofclk, s_clkofns; static uint64_t s_clkofsec; static int s_rand_arr[RAND_ARRAY_SIZE]; static double s_latest_avglat = 0.0; static struct timeval s_tvbase; static int s_ncpus; static void *s_buf = NULL; static unsigned int s_randseed; static void *buf_create(int); static void buf_release(void *); static int dependent_read(void *, int, int, int); static uint64_t rdtsc(); static void print_usage(const char *exec_name) { char buffer[PATH_MAX]; strncpy(buffer, exec_name, PATH_MAX); buffer[PATH_MAX - 1] = 0; printf("Usage: %s [option(s)]\n", basename(buffer)); printf("Options:\n" " -h: print helps\n" " -a: the cpu where allocates memory on the associated node\n" " -c: the cpu where creates a thread to access memory.\n" " -t: the seconds for measuring.\n" " -s: the random seed to build random address array (just for reproducing).\n"); printf("\nFor example:\n" " 1. Generate LMA (Local Memory Access) on cpu1 for 10s measuring:\n" " %s -a 1 -c 1 -t 10\n" " 2. Generate RMA (Remote Memory Access) from cpu1 to cpu0 for 10s measuring:\n" " %s -a 0 -c 1 -t 10\n", basename(buffer), basename(buffer)); } /* * Get the interval in milliseconds between 2 timestamps. */ static unsigned int msdiff(struct timeval *tva, struct timeval *tvb) { time_t sdiff = tva->tv_sec - tvb->tv_sec; suseconds_t udiff = tva->tv_usec - tvb->tv_usec; if (sdiff < 0) { return (0); } if (udiff < 0) { udiff += MICROSEC; sdiff--; } if (sdiff < 0) { return (0); } if (sdiff >= (MAX_VALUE / MS_SEC)) { return ((unsigned int)MAX_VALUE); } return ((unsigned int)(sdiff * MS_SEC + udiff / MS_SEC)); } /* * Get the current timestamp and convert it to milliseconds */ uint64_t current_ms(void) { struct timeval tvnow; (void) gettimeofday(&tvnow, 0); return (msdiff(&tvnow, &s_tvbase)); } /* * Bind current thread to a cpu or unbind current thread * from a cpu. */ static int processor_bind(int cpu) { cpu_set_t cs; CPU_ZERO (&cs); CPU_SET (cpu, &cs); if (sched_setaffinity(0, sizeof (cs), &cs) < 0) { return (-1); } return (0); } static int processor_unbind(void) { cpu_set_t cs; int i; CPU_ZERO (&cs); for (i = 0; i < s_ncpus; i++) { CPU_SET (i, &cs); } if (sched_setaffinity(0, sizeof (cs), &cs) < 0) { return (-1); } return (0); } /* * Check the cpu name in proc info. Intel CPUs always have @ x.y * Ghz and that is the TSC frequency. */ static int calibrate_cpuinfo(double *nsofclk, double *clkofns, uint64_t *clkofsec) { FILE *f; char *line = NULL, unit[11]; size_t len = 0; double freq = 0.0; if ((f = fopen(CPUINFO_PATH, "r")) == NULL) { return (-1); } while (getline(&line, &len, f) > 0) { if (strncmp(line, "model name", sizeof ("model name") - 1) != 0) { continue; } if (sscanf(line + strcspn(line, "@") + 1, "%lf%10s", &freq, unit) == 2) { if (strcasecmp(unit, "GHz") == 0) { freq *= GHZ; } else if (strcasecmp(unit, "Mhz") == 0) { freq *= MHZ; } break; } } free(line); fclose(f); if (freq == 0.0) { return (-1); } *clkofsec = freq; *nsofclk = (double)NS_SEC / *clkofsec; *clkofns = (double)1.0 / *nsofclk; return (0); } /* * On all recent Intel CPUs, the TSC frequency is always * the highest p-state. So get that frequency from sysfs. * e.g. 2262000 */ static int calibrate_cpufreq(double *nsofclk, double *clkofns, uint64_t *clkofsec) { int fd, i; char buf[32]; uint64_t freq; if ((fd = open(CPU0_CPUFREQ_PATH, O_RDONLY)) < 0) { return (-1); } if ((i = read(fd, buf, sizeof (buf) - 1)) <= 0) { close(fd); return (-1); } close(fd); buf[i] = 0; if ((freq = atoll(buf)) == 0) { return (-1); } *clkofsec = freq * KHZ; *nsofclk = (double)NS_SEC / *clkofsec; *clkofns = (double)1.0 / *nsofclk; return (0); } /* * Measure how many TSC cycles in a second and how many nanoseconds * in a TSC cycle. */ static void calibrate_by_tsc(double *nsofclk, double *clkofns, uint64_t *clkofsec) { uint64_t start_ms, end_ms, diff_ms; uint64_t start_tsc, end_tsc; /* * Bind current thread to cpu0 to ensure the * thread will not be migrated to another cpu * while the rdtsc runs. */ if (processor_bind(0) != 0) { return; } /* * Make sure the start_ms is at the beginning of * one millisecond. */ end_ms = current_ms(); while ((start_ms = current_ms()) == end_ms) {} start_tsc = rdtsc(); while ((end_ms = current_ms()) < (start_ms + 100)) {} end_tsc = rdtsc(); diff_ms = end_ms - start_ms; *nsofclk = (double)(diff_ms * NS_MS) / (double)(end_tsc - start_tsc); *clkofns = (double)1.0 / *nsofclk; *clkofsec = (uint64_t)((double)NS_SEC / *nsofclk); /* * Unbind current thread from cpu0 once the measurement completed. */ (void) processor_unbind(); } void calibrate(void) { if (calibrate_cpuinfo(&s_nsofclk, &s_clkofns, &s_clkofsec) == 0) { return; } if (calibrate_cpufreq(&s_nsofclk, &s_clkofns, &s_clkofsec) == 0) { return; } calibrate_by_tsc(&s_nsofclk, &s_clkofns, &s_clkofsec); } static void sigint_handler(int sig) { switch (sig) { case SIGINT: (void) signal(SIGINT, sigint_handler); break; case SIGHUP: (void) signal(SIGINT, sigint_handler); break; case SIGQUIT: (void) signal(SIGINT, sigint_handler); break; case SIGPIPE: (void) signal(SIGINT, sigint_handler); break; case SIGTERM: (void) signal(SIGINT, sigint_handler); break; } printf("-------------------------\n"); printf("%24s\n", "*** Terminated! ***"); if (s_latest_avglat > 0.0) { printf("%9s %13.1f\n\n", "Average", s_latest_avglat); } else { printf("%9s %13.1f\n\n", "Average", 0.0); } if (s_buf != NULL) { buf_release(s_buf); } exit (0); } int main(int argc, char *argv[]) { int cpu_alloc = -1, cpu_consumer = -1; int meas_sec = MEAS_TIME_DEFAULT; int ret = -1; char c; s_randseed = 0; optind = 1; opterr = 0; while ((c = getopt(argc, argv, "a:c:hf:t:s:")) != EOF) { switch (c) { case 'h': print_usage(argv[0]); ret = 0; goto L_EXIT0; case 'a': cpu_alloc = atoi(optarg); break; case 'c': cpu_consumer = atoi(optarg); break; case 't': meas_sec = atoi(optarg); break; case 's': s_randseed = atoi(optarg); break; case ':': printf("Missed argument for option %c.\n", optopt); print_usage(argv[0]); goto L_EXIT0; case '?': printf("Unrecognized option %c.\n", optopt); print_usage(argv[0]); goto L_EXIT0; } } s_ncpus = sysconf(_SC_NPROCESSORS_CONF); if (cpu_alloc == -1) { printf("Missed argument for option '-a'.\n"); print_usage(argv[0]); goto L_EXIT0; } if (cpu_consumer == -1) { printf("Missed argument for option '-c'.\n"); print_usage(argv[0]); goto L_EXIT0; } if ((signal(SIGINT, sigint_handler) == SIG_ERR) || (signal(SIGHUP, sigint_handler) == SIG_ERR) || (signal(SIGQUIT, sigint_handler) == SIG_ERR) || (signal(SIGTERM, sigint_handler) == SIG_ERR) || (signal(SIGPIPE, sigint_handler) == SIG_ERR)) { goto L_EXIT0; } gettimeofday(&s_tvbase, 0); calibrate(); if ((s_buf = buf_create(cpu_alloc)) == NULL) { printf("Failed to create buffer.\n"); goto L_EXIT0; } if (dependent_read(s_buf, cpu_consumer, cpu_alloc, meas_sec) != 0) { printf("Failed to dependent read.\n"); goto L_EXIT0; } ret = 0; L_EXIT0: if (s_buf != NULL) { buf_release(s_buf); } return (ret); } static int last_free_elem(void) { int i, cnt = 0; for (i = 0; i < RAND_ARRAY_SIZE; i++) { if (s_rand_arr[i] == INVALID_RAND) { cnt++; if (cnt > 1) { return (0); } } } if (cnt == 1) { return (1); } return (0); } static void rand_array_init(void) { int i, r, index = 0; if (s_randseed == 0) { s_randseed = time(0); } srand(s_randseed); for (i = 0; i < RAND_ARRAY_SIZE; i++) { s_rand_arr[i] = INVALID_RAND; } while (1) { for (;;) { r = rand() % RAND_ARRAY_SIZE; if (s_rand_arr[r] == INVALID_RAND) { break; } } if ((s_rand_arr[index] == INVALID_RAND) && (index != r)) { s_rand_arr[index] = r; index = r; } if (last_free_elem()) { s_rand_arr[index] = RAND_ARRAY_SIZE; break; } } } static void rand_buf_init(void *buf, int size) { int nblk = size / (RAND_ARRAY_SIZE * LINE_SIZE); int i, j; uint64_t **p, **blk_start, **end = NULL; p = (uint64_t **)buf; for (i = 0; i < nblk; i++) { blk_start = p; for (j = 0; j < RAND_ARRAY_SIZE; j++) { if (s_rand_arr[j] == RAND_ARRAY_SIZE) { end = p; } *p = (uint64_t *)((char *)blk_start + (s_rand_arr[j] * LINE_SIZE)); p = (uint64_t **)((char *)p + LINE_SIZE); } } if (end != NULL) { *end = (uint64_t *)buf; } } static void buf_init(void *buf, int size) { rand_array_init(); rand_buf_init(buf, size); } static void * buf_create(int cpu_alloc) { void *buf; if (processor_bind(cpu_alloc) != 0) { return (NULL); } buf= (void *)mmap(0, BUF_SIZE, PROT_READ | PROT_WRITE | PROT_EXEC,MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); if (buf != NULL) { buf_init(buf, BUF_SIZE); } return (buf); } static void buf_release(void *buf) { (void) munmap(buf, BUF_SIZE); } static uint64_t rdtsc() { uint64_t var; uint32_t hi, lo; __asm volatile ("rdtsc" : "=a" (lo), "=d" (hi)); /* LINTED E_VAR_USED_BEFORE_SET */ var = ((uint64_t)hi << 32) | lo; return (var); } static void buf_read(void *buf, int read_num) { asm volatile ( "xor %0, %0\n\t" "LOOP1:\n\t" "mov (%1),%1\n\t" "inc %0\n\t" "cmp %2,%0\n\t" "jb LOOP1\n\t" "STOP:\n\t" ::"b"(0), "d"(buf), "r"(read_num) ); } static void latency_calculate(uint64_t count, uint64_t dur_cyc, uint64_t total_cyc) { double sec, lat; sec = (double)total_cyc / (double)s_clkofsec; lat = ((double)dur_cyc * s_nsofclk) / (double)count; printf("%8.1fs %13.1f\n", sec, lat); fflush(stdout); } static int dependent_read(void *buf, int cpu_consumer, int cpu_alloc, int meas_sec) { uint64_t total_count = 0, dur_count = 0; uint64_t start_tsc, end_tsc, prev_tsc; uint64_t run_cyc, total_cyc, dur_cyc; run_cyc = (uint64_t)((uint64_t)meas_sec * (uint64_t)((double)(NS_SEC) * s_clkofns)); if (processor_bind(cpu_consumer) != 0) { return (-1); } start_tsc = rdtsc(); end_tsc = start_tsc; prev_tsc = start_tsc; fprintf(stdout, "\n!!! The reported latency is not the official data\n"); fprintf(stdout, " from Intel, it's just a tool to test numatop !!!\n"); fprintf(stdout, "\nGenerating memory access from cpu%d to cpu%d for ~%ds ...\n", cpu_consumer, cpu_alloc, meas_sec); fprintf(stdout, "(random seed to build random address array is %d.)\n", s_randseed); printf("\n%9s %13s\n", "Time", "Latency(ns)"); printf("-------------------------\n"); while (1) { total_cyc = end_tsc - start_tsc; dur_cyc = end_tsc - prev_tsc; if (dur_cyc >= s_clkofsec) { latency_calculate(dur_count, dur_cyc, total_cyc); prev_tsc = rdtsc(); dur_count = 0; } if (total_cyc >= run_cyc) { break; } if (total_count > 0) { s_latest_avglat = ((double)total_cyc * s_nsofclk) / (double)total_count; } buf_read(buf, READ_NUM); dur_count += READ_NUM; total_count += READ_NUM; end_tsc = rdtsc(); } printf("-------------------------\n"); if (total_count > 0) { printf("%9s %13.1f\n\n", "Average", ((double)total_cyc * s_nsofclk) / (double)total_count); } else { printf("%9s %13.1f\n\n", "Average", 0.0); } return (0); } numatop/AUTHORS0000664000175000017500000000003412633407552013163 0ustar jinyaojinyaoJin Yao (yao.jin@intel.com)