/* * BSD LICENSE * * Copyright(c) 2014-2020 Intel Corporation. All rights reserved. * All rights reserved. * * 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. * */ /** * @brief Platform QoS sample LLC occupancy monitoring application * */ #include #include #include #include #include #include #include #include "pqos.h" /** * Defines */ #define PQOS_MAX_CORES 1024 #define PQOS_MAX_PIDS 16 #define PQOS_MAX_MON_EVENTS 1 /** * Number of cores that are selected in config string * for monitoring LLC occupancy */ static int sel_monitor_num = 0; /** * The mask to tell which events to display */ static enum pqos_mon_event sel_events_max = (enum pqos_mon_event)0; /** * Maintains a table of core, event, number of events that are * selected in config string for monitoring */ static struct { unsigned core; struct pqos_mon_data *pgrp; enum pqos_mon_event events; } sel_monitor_core_tab[PQOS_MAX_CORES]; static struct pqos_mon_data *m_mon_grps[PQOS_MAX_CORES]; /** * Maintains a table of process id, event, number of events that are selected * in config string for monitoring LLC occupancy */ static struct { pid_t pid; struct pqos_mon_data *pgrp; enum pqos_mon_event events; } sel_monitor_pid_tab[PQOS_MAX_PIDS]; /** * Maintains the number of process id's you want to track */ static int sel_process_num = 0; /** * Flag to determine which library interface to use */ static enum pqos_interface interface = PQOS_INTER_MSR; static void stop_monitoring(void); /** * @brief CTRL-C handler for infinite monitoring loop * * @param [in] signo signal number */ static void __attribute__((noreturn)) monitoring_ctrlc(int signo) { printf("\nExiting[%d]...\n", signo); stop_monitoring(); if (pqos_fini() != PQOS_RETVAL_OK) { printf("Error shutting down PQoS library!\n"); exit(EXIT_FAILURE); } exit(EXIT_SUCCESS); } /** * @brief Scale byte value up to KB * * @param [in] bytes value to be scaled up * @return scaled up value in KB's */ static inline double bytes_to_kb(const double bytes) { return bytes / 1024.0; } /** * @brief Scale byte value up to MB * * @param [in] bytes value to be scaled up * @return scaled up value in MB's */ static inline double bytes_to_mb(const double bytes) { return bytes / (1024.0 * 1024.0); } /** * @brief Check to determine if processes or cores are monitored * * @return Process monitoring mode status * @retval 0 monitoring cores * @retval 1 monitoring processes */ static inline int process_mode(void) { return (sel_process_num <= 0) ? 0 : 1; } /** * @brief Verifies and translates monitoring config string into * internal monitoring configuration. * * @param [in] argc Number of arguments in input command * @param [in] argv Input arguments for LLC occupancy monitoring */ static void monitoring_get_input(int argc, char *argv[]) { int num_args, num_opts = 1, i = 0, sel_pid = 0, help = 0; for (i = 0; i < argc; i++) { if (!strcmp(argv[i], "-p")) { sel_pid = 1; num_opts++; } else if (!strcmp(argv[i], "-I")) { interface = PQOS_INTER_OS; num_opts++; } else if (!strcmp(argv[i], "-H") || !strcmp(argv[i], "-h")) { help = 1; num_opts++; } } /* Ensure OS interface selected if monitoring tasks */ if (sel_pid && interface == PQOS_INTER_MSR) { printf("Error: PID monitoring requires OS interface " "selection!\nPlease use the -I option.\n"); help = 1; } num_args = (argc - num_opts); if (help) { printf("Usage: %s [ ...]\n" " %s -I -p [ ...]\n", argv[0], argv[0]); printf("Eg : %s 1 2 6\n " "%s -I -p 3564 7638 356\n" "Notes:\n " "-h help\n " "-I select library OS interface\n " "-p select process ID's to monitor LLC occupancy" "\n\n", argv[0], argv[0]); exit(EXIT_SUCCESS); } else if (num_args == 0) { sel_monitor_num = 0; } else { if (sel_pid) { if (num_args > PQOS_MAX_PIDS) num_args = PQOS_MAX_PIDS; for (i = 0; i < num_args; i++) { m_mon_grps[i] = malloc(sizeof(**m_mon_grps)); sel_monitor_pid_tab[i].pgrp = m_mon_grps[i]; sel_monitor_pid_tab[i].pid = (unsigned) atoi(argv[num_opts + i]); } sel_process_num = (int) num_args; } else { if (num_args > PQOS_MAX_CORES) num_args = PQOS_MAX_CORES; for (i = 0; i < num_args; i++) { m_mon_grps[i] = malloc(sizeof(**m_mon_grps)); sel_monitor_core_tab[i].pgrp = m_mon_grps[i]; sel_monitor_core_tab[i].core = (unsigned) atoi(argv[num_opts + i]); } sel_monitor_num = (int) num_args; } } } /** * @brief Starts monitoring on selected cores/PIDs * * @param [in] cpu_info cpu information structure * @param [in] cap_mon monitoring capabilities structure * * @return Operation status * @retval 0 OK * @retval -1 error */ static int setup_monitoring(const struct pqos_cpuinfo *cpu_info, const struct pqos_capability * const cap_mon) { unsigned i; const enum pqos_mon_event perf_events = (enum pqos_mon_event) (PQOS_PERF_EVENT_IPC | PQOS_PERF_EVENT_LLC_MISS); for (i = 0; (unsigned)i < cap_mon->u.mon->num_events; i++) sel_events_max |= (cap_mon->u.mon->events[i].type); /* Remove perf events IPC and LLC MISSES */ sel_events_max &= ~perf_events; if (sel_monitor_num == 0 && sel_process_num == 0) { for (i = 0; i < cpu_info->num_cores; i++) { unsigned lcore = cpu_info->cores[i].lcore; sel_monitor_core_tab[sel_monitor_num].core = lcore; sel_monitor_core_tab[sel_monitor_num].events = sel_events_max; m_mon_grps[sel_monitor_num] = malloc(sizeof(**m_mon_grps)); sel_monitor_core_tab[sel_monitor_num].pgrp = m_mon_grps[sel_monitor_num]; sel_monitor_num++; } } if (!process_mode()) { for (i = 0; i < (unsigned) sel_monitor_num; i++) { unsigned lcore = sel_monitor_core_tab[i].core; int ret; ret = pqos_mon_start(1, &lcore, sel_events_max, NULL, sel_monitor_core_tab[i].pgrp); if (ret != PQOS_RETVAL_OK) { printf("Monitoring start error on core %u," "status %d\n", lcore, ret); return ret; } } } else { for (i = 0; i < (unsigned) sel_process_num; i++) { pid_t pid = sel_monitor_pid_tab[i].pid; int ret; ret = pqos_mon_start_pids(1, &pid, PQOS_MON_EVENT_L3_OCCUP, NULL, sel_monitor_pid_tab[i].pgrp); if (ret != PQOS_RETVAL_OK) { printf("Monitoring start error on pid %u," "status %d\n", pid, ret); return ret; } } } return PQOS_RETVAL_OK; } /** * @brief Stops monitoring on selected cores * */ static void stop_monitoring(void) { unsigned i, mon_number = 0; if (!process_mode()) mon_number = (unsigned) sel_monitor_num; else mon_number = (unsigned) sel_process_num; for (i = 0; i < mon_number; i++) { int ret; ret = pqos_mon_stop(m_mon_grps[i]); if (ret != PQOS_RETVAL_OK) printf("Monitoring stop error!\n"); free(m_mon_grps[i]); } } /** * @brief Reads monitoring event data */ static void monitoring_loop(void) { unsigned mon_number = 0; int ret = PQOS_RETVAL_OK; int i = 0; if (signal(SIGINT, monitoring_ctrlc) == SIG_ERR) printf("Failed to catch SIGINT!\n"); if (!process_mode()) mon_number = (unsigned) sel_monitor_num; else mon_number = (unsigned) sel_process_num; while (1) { ret = pqos_mon_poll(m_mon_grps, (unsigned)mon_number); if (ret != PQOS_RETVAL_OK) { printf("Failed to poll monitoring data!\n"); return; } if (!process_mode()) { printf(" CORE LLC[KB] MBL[MB] MBR[MB]\n"); for (i = 0; i < sel_monitor_num; i++) { const struct pqos_event_values *pv = &m_mon_grps[i]->values; double llc = bytes_to_kb(pv->llc); double mbr = bytes_to_mb(pv->mbm_remote_delta); double mbl = bytes_to_mb(pv->mbm_local_delta); printf("%8u %10.1f %10.1f %10.1f\n", m_mon_grps[i]->cores[0], llc, mbl, mbr); } } else { printf("PID LLC[KB]\n"); for (i = 0; i < sel_process_num; i++) { const struct pqos_event_values *pv = &m_mon_grps[i]->values; double llc = bytes_to_kb(pv->llc); printf("%6d %10.1f\n", m_mon_grps[i]->pids[0], llc); } } printf("\nPress Enter to continue or Ctrl+c to exit"); if (getchar() != '\n') break; printf("\e[1;1H\e[2J"); } } int main(int argc, char *argv[]) { struct pqos_config config; const struct pqos_cpuinfo *p_cpu = NULL; const struct pqos_cap *p_cap = NULL; int ret, exit_val = EXIT_SUCCESS; const struct pqos_capability *cap_mon = NULL; /* Get input from user */ monitoring_get_input(argc, argv); memset(&config, 0, sizeof(config)); config.fd_log = STDOUT_FILENO; config.verbose = 0; config.interface = interface; /* PQoS Initialization - Check and initialize CAT and CMT capability */ ret = pqos_init(&config); if (ret != PQOS_RETVAL_OK) { printf("Error initializing PQoS library!\n"); exit_val = EXIT_FAILURE; goto error_exit; } /* Get CMT capability and CPU info pointer */ ret = pqos_cap_get(&p_cap, &p_cpu); if (ret != PQOS_RETVAL_OK) { printf("Error retrieving PQoS capabilities!\n"); exit_val = EXIT_FAILURE; goto error_exit; } ret = pqos_cap_get_type(p_cap, PQOS_CAP_TYPE_MON, &cap_mon); if (ret != PQOS_RETVAL_OK) { printf("Error retrieving monitoring capabilities\n"); exit_val = EXIT_FAILURE; goto error_exit; } /* Setup the monitoring resources */ ret = setup_monitoring(p_cpu, cap_mon); if (ret != PQOS_RETVAL_OK) { printf("Error Setting up monitoring!\n"); exit_val = EXIT_FAILURE; goto error_exit; } /* Start Monitoring */ monitoring_loop(); /* Stop Monitoring */ stop_monitoring(); error_exit: ret = pqos_fini(); if (ret != PQOS_RETVAL_OK) printf("Error shutting down PQoS library!\n"); return exit_val; }