/****************************************************************************** * tools/xenbaked.c * * Tool for collecting raw trace buffer data from Xen and * performing some accumulation operations and other processing * on it. * * Copyright (C) 2004 by Intel Research Cambridge * Copyright (C) 2005 by Hewlett Packard, Palo Alto and Fort Collins * Copyright (C) 2006 by Hewlett Packard Fort Collins * * Authors: Diwaker Gupta, diwaker.gupta@hp.com * Rob Gardner, rob.gardner@hp.com * Lucy Cherkasova, lucy.cherkasova.hp.com * Much code based on xentrace, authored by Mark Williamson, * mark.a.williamson@intel.com * Date: November, 2005 * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; under version 2 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include #include #include #include #include #include #include #define PERROR(_m, _a...) \ do { \ int __saved_errno = errno; \ fprintf(stderr, "ERROR: " _m " (%d = %s)\n" , ## _a , \ __saved_errno, strerror(__saved_errno)); \ errno = __saved_errno; \ } while (0) typedef struct { int counter; } atomic_t; #define _atomic_read(v) ((v).counter) #include #include "xenbaked.h" /***** Compile time configuration of defaults ********************************/ /* when we've got more records than this waiting, we log it to the output */ #define NEW_DATA_THRESH 1 /* sleep for this long (milliseconds) between checking the trace buffers */ #define POLL_SLEEP_MILLIS 100 /* Size of time period represented by each sample */ #define MS_PER_SAMPLE 100 /* CPU Frequency */ #define MHZ #define CPU_FREQ 2660 MHZ /***** The code **************************************************************/ typedef struct settings_st { struct timespec poll_sleep; unsigned long new_data_thresh; unsigned long ms_per_sample; double cpu_freq; } settings_t; struct t_struct { const struct t_info *t_info; /* Structure with information about individual buffers */ struct t_buf **meta; /* Pointers to trace buffer metadata */ unsigned char **data; /* Pointers to trace buffer data areas */ }; settings_t opts; int interrupted = 0; /* gets set if we get a SIGHUP */ int rec_count = 0; int wakeups = 0; time_t start_time; int dom0_flips = 0; _new_qos_data *new_qos; _new_qos_data **cpu_qos_data; int global_cpu; uint64_t global_now; // array of currently running domains, indexed by cpu int *running = NULL; // number of cpu's on this platform int NCPU = 0; static void advance_next_datapoint(uint64_t); static void alloc_qos_data(int ncpu); static int process_record(int, struct t_rec *); static void qos_kill_thread(int domid); static void init_current(int ncpu) { running = calloc(ncpu, sizeof(int)); NCPU = ncpu; printf("Initialized with %d %s\n", ncpu, (ncpu == 1) ? "cpu" : "cpu's"); } static int is_current(int domain, int cpu) { // int i; // for (i=0; icycles, x->event, x->data[0]); } #endif /** * millis_to_timespec - convert a time in milliseconds to a struct timespec * @millis: time interval in milliseconds */ static struct timespec millis_to_timespec(unsigned long millis) { struct timespec spec; spec.tv_sec = millis / 1000; spec.tv_nsec = (millis % 1000) * 1000; return spec; } typedef struct { int event_count; int event_id; char *text; } stat_map_t; stat_map_t stat_map[] = { { 0, 0, "Other" }, { 0, TRC_SCHED_DOM_ADD, "Add Domain" }, { 0, TRC_SCHED_DOM_REM, "Remove Domain" }, { 0, TRC_SCHED_SLEEP, "Sleep" }, { 0, TRC_SCHED_WAKE, "Wake" }, { 0, TRC_SCHED_BLOCK, "Block" }, { 0, TRC_SCHED_SWITCH, "Switch" }, { 0, TRC_SCHED_S_TIMER_FN, "Timer Func"}, { 0, TRC_SCHED_SWITCH_INFPREV, "Switch Prev" }, { 0, TRC_SCHED_SWITCH_INFNEXT, "Switch Next" }, { 0, TRC_MEM_PAGE_GRANT_MAP, "Page Map" }, { 0, TRC_MEM_PAGE_GRANT_UNMAP, "Page Unmap" }, { 0, TRC_MEM_PAGE_GRANT_TRANSFER, "Page Transfer" }, { 0, 0, 0 } }; static void check_gotten_sum(void) { #if 0 uint64_t sum, ns; extern uint64_t total_ns_gotten(uint64_t*); double percent; int i; for (i=0; i ns_gotten = %7.3f%%\n", percent); } #endif } static void dump_stats(void) { stat_map_t *smt = stat_map; time_t end_time, run_time; time(&end_time); run_time = end_time - start_time; printf("Event counts:\n"); while (smt->text != NULL) { printf("%08d\t%s\n", smt->event_count, smt->text); smt++; } printf("processed %d total records in %d seconds (%ld per second)\n", rec_count, (int)run_time, (long)(rec_count/run_time)); printf("woke up %d times in %d seconds (%ld per second)\n", wakeups, (int) run_time, (long)(wakeups/run_time)); check_gotten_sum(); } static void log_event(int event_id) { stat_map_t *smt = stat_map; // printf("event_id = 0x%x\n", event_id); while (smt->text != NULL) { if (smt->event_id == event_id) { smt->event_count++; return; } smt++; } if (smt->text == NULL) stat_map[0].event_count++; // other } int virq_port; xc_evtchn *xce_handle = NULL; /* Returns the event channel handle. */ /* Stolen from xenstore code */ static int eventchn_init(void) { int rc; // to revert to old way: if (0) return -1; xce_handle = xc_evtchn_open(NULL, 0); if (xce_handle == NULL) perror("Failed to open evtchn device"); if ((rc = xc_evtchn_bind_virq(xce_handle, VIRQ_TBUF)) == -1) perror("Failed to bind to domain exception virq port"); virq_port = rc; return xce_handle == NULL ? -1 : 0; } static void wait_for_event(void) { int ret; fd_set inset; evtchn_port_t port; struct timeval tv; int evtchn_fd; if (xce_handle == NULL) { nanosleep(&opts.poll_sleep, NULL); return; } evtch
# This file is dual licensed under the terms of the Apache License, Version
# 2.0, and the BSD License. See the LICENSE file in the root of this repository
# for complete details.

from __future__ import absolute_import, division, print_function

INCLUDES = """
#include <openssl/crypto.h>
"""

TYPES = """
static const long Cryptography_HAS_LOCKING_CALLBACKS;
static const long Cryptography_HAS_MEM_FUNCTIONS;
static const long Cryptography_HAS_OPENSSL_CLEANUP;

static const int SSLEAY_VERSION;
static const int SSLEAY_CFLAGS;
static const int SSLEAY_PLATFORM;
static const int SSLEAY_DIR;
static const int SSLEAY_BUILT_ON;
static const int OPENSSL_VERSION;
static const int OPENSSL_CFLAGS;
static const int OPENSSL_BUILT_ON;
static const int OPENSSL_PLATFORM;
static const int OPENSSL_DIR;
static const int CRYPTO_MEM_CHECK_ON;
static const int CRYPTO_MEM_CHECK_OFF;
static const int CRYPTO_MEM_CHECK_ENABLE;
static const int CRYPTO_MEM_CHECK_DISABLE;
"""

FUNCTIONS = """
int CRYPTO_mem_ctrl(int);

void OPENSSL_cleanup(void);

/* as of 1.1.0 OpenSSL does its own locking *angelic chorus*. This function
   is now a noop macro. We can delete this once we drop 1.0.2 support. */
void (*CRYPTO_get_locking_callback(void))(int, int, const char *, int);

/* SSLeay was removed in 1.1.0 */
unsigned long SSLeay(void);
const char *SSLeay_version(int);
/* these functions were added to replace the SSLeay functions in 1.1.0 */
unsigned long OpenSSL_version_num(void);
const char *OpenSSL_version(int);

/* this is a macro in 1.1.0 */
void *OPENSSL_malloc(size_t);
void OPENSSL_free(void *);


/* Signature changed significantly in 1.1.0, only expose there for sanity */
int Cryptography_CRYPTO_set_mem_functions(
    void *(*)(size_t, const char *, int),
    void *(*)(void *, size_t, const char *, int),
    void (*)(void *, const char *, int));

void *Cryptography_malloc_wrapper(size_t, const char *, int);
void *Cryptography_realloc_wrapper(void *, size_t, const char *, int);
void Cryptography_free_wrapper(void *, const char *, int);
"""

CUSTOMIZATIONS = """
/* In 1.1.0 SSLeay has finally been retired. We bidirectionally define the
   values so you can use either one. This is so we can use the new function
   names no matter what OpenSSL we're running on, but users on older pyOpenSSL
   releases won't see issues if they're running OpenSSL 1.1.0 */
#if !defined(SSLEAY_VERSION)
# define SSLeay                  OpenSSL_version_num
# define SSLeay_version          OpenSSL_version
# define SSLEAY_VERSION_NUMBER   OPENSSL_VERSION_NUMBER
# define SSLEAY_VERSION          OPENSSL_VERSION
# define SSLEAY_CFLAGS           OPENSSL_CFLAGS
# define SSLEAY_BUILT_ON         OPENSSL_BUILT_ON
# define SSLEAY_PLATFORM         OPENSSL_PLATFORM
# define SSLEAY_DIR              OPENSSL_DIR
#endif
#if !defined(OPENSSL_VERSION)
# define OpenSSL_version_num     SSLeay
# define OpenSSL_version         SSLeay_version
# define OPENSSL_VERSION         SSLEAY_VERSION
# define OPENSSL_CFLAGS          SSLEAY_CFLAGS
# define OPENSSL_BUILT_ON        SSLEAY_BUILT_ON
# define OPENSSL_PLATFORM        SSLEAY_PLATFORM
# define OPENSSL_DIR             SSLEAY_DIR
#endif
#if CRYPTOGRAPHY_OPENSSL_LESS_THAN_110
static const long Cryptography_HAS_LOCKING_CALLBACKS = 1;
#else
static const long Cryptography_HAS_LOCKING_CALLBACKS = 0;
#endif

#if CRYPTOGRAPHY_OPENSSL_LESS_THAN_110
static const long Cryptography_HAS_OPENSSL_CLEANUP = 0;

void (*OPENSSL_cleanup)(void) = NULL;

/* This function has a significantly different signature pre-1.1.0. since it is
 * for testing only, we don't bother to expose it on older OpenSSLs.
 */
static const long Cryptography_HAS_MEM_FUNCTIONS = 0;
int (*Cryptography_CRYPTO_set_mem_functions)(
    void *(*)(size_t, const char *, int),
    void *(*)(void *, size_t, const char *, int),
    void (*)(void *, const char *, int)) = NULL;

#else
static const long Cryptography_HAS_OPENSSL_CLEANUP = 1;
static const long Cryptography_HAS_MEM_FUNCTIONS = 1;

int Cryptography_CRYPTO_set_mem_functions(
    void *(*m)(size_t, const char *, int),
    void *(*r)(void *, size_t, const char *, int),
    void (*f)(void *, const char *, int)
) {
    return CRYPTO_set_mem_functions(m, r, f);
}
#endif

void *Cryptography_malloc_wrapper(size_t size, const char *path, int line) {
    return malloc(size);
}

void *Cryptography_realloc_wrapper(void *ptr, size_t size, const char *path,
                                   int line) {
    return realloc(ptr, size);
}

void Cryptography_free_wrapper(void *ptr, const char *path, int line) {
    free(ptr);
}
"""
s_init_domain(domid, idx); } new_qos = saved_qos; } // give index of this domain in the qos data array static int indexof(int domid) { int idx; xc_dominfo_t dominfo[NDOMAINS]; xc_interface *xc_handle; int ndomains; if (domid < 0) { // shouldn't happen printf("bad domain id: %d\r\n", domid); return 0; } for (idx=0; idxdomain_info[idx].id == domid) && new_qos->domain_info[idx].in_use) return idx; // not found, make a new entry for (idx=0; idxdomain_info[idx].in_use == 0) { global_init_domain(domid, idx); return idx; } // call domaininfo hypercall to try and garbage collect unused entries xc_handle = xc_interface_open(0,0,0); ndomains = xc_domain_getinfo(xc_handle, 0, NDOMAINS, dominfo); xc_interface_close(xc_handle); // for each domain in our data, look for it in the system dominfo structure // and purge the domain's data from our state if it does not exist in the // dominfo structure for (idx=0; idxdomain_info[idx].id; int jdx; for (jdx=0; jdxdomain_info[idx].in_use == 0) { global_init_domain(domid, idx); return idx; } // still no space found, so bail fprintf(stderr, "out of space in domain table, increase NDOMAINS\r\n"); exit(2); } static int domain_runnable(int domid) { return new_qos->domain_info[indexof(domid)].runnable; } static void update_blocked_time(int domid, uint64_t now) { uint64_t t_blocked; int id = indexof(domid); if (new_qos->domain_info[id].blocked_start_time != 0) { if (now >= new_qos->domain_info[id].blocked_start_time) t_blocked = now - new_qos->domain_info[id].blocked_start_time; else t_blocked = now + (~0ULL - new_qos->domain_info[id].blocked_start_time); new_qos->qdata[new_qos->next_datapoint].ns_blocked[id] += t_blocked; } if (domain_runnable(domid)) new_qos->domain_info[id].blocked_start_time = 0; else new_qos->domain_info[id].blocked_start_time = now; } // advance to next datapoint for all domains static void advance_next_datapoint(uint64_t now) { int new, old, didx; old = new_qos->next_datapoint; new = QOS_INCR(old); new_qos->next_datapoint = new; // memset(&new_qos->qdata[new], 0, sizeof(uint64_t)*(2+5*NDOMAINS)); for (didx = 0; didx < NDOMAINS; didx++) { new_qos->qdata[new].ns_gotten[didx] = 0; new_qos->qdata[new].ns_allocated[didx] = 0; new_qos->qdata[new].ns_waiting[didx] = 0; new_qos->qdata[new].ns_blocked[didx] = 0; new_qos->qdata[new].switchin_count[didx] = 0; new_qos->qdata[new].io_count[didx] = 0; } new_qos->qdata[new].ns_passed = 0; new_qos->qdata[new].lost_records = 0; new_qos->qdata[new].flip_free_periods = 0; new_qos->qdata[new].timestamp = now; } static void qos_update_thread(int cpu, int domid, uint64_t now) { int n, id; uint64_t last_update_time, start; int64_t time_since_update, run_time = 0; id = indexof(domid); n = new_qos->next_datapoint; last_update_time = new_qos->domain_info[id].last_update_time; time_since_update = now - last_update_time; if (time_since_update < 0) { // what happened here? either a timestamp wraparound, or more likely, // a slight inconsistency among timestamps from various cpu's if (-time_since_update < billion) { // fairly small difference, let's just adjust 'now' to be a little // beyond last_update_time time_since_update = -time_since_update; } else if ( ((~0ULL - last_update_time) < billion) && (now < billion) ) { // difference is huge, must be a wraparound // last_update time should be "near" ~0ULL, // and now should be "near" 0 time_since_update = now + (~0ULL - last_update_time); printf("time wraparound\n"); } else { // none of the above, may be an out of order record // no good solution, just ignore and update again later return; } } new_qos->domain_info[id].last_update_time = now; if (new_qos->domain_info[id].runnable_at_last_update && is_current(domid, cpu)) { start = new_qos->domain_info[id].start_time; if (start > now) { // wrapped around run_time = now + (~0ULL - start); // this could happen if there is nothing going on within a cpu; // in this case the idle domain would run forever // printf("warning: start > now\n"); } else run_time = now - start; // if (run_time < 0) // should not happen // printf("warning: run_time < 0; start = %lld now= %lld\n", start, now); new_qos->domain_info[id].ns_oncpu_since_boot += run_time; new_qos->domain_info[id].start_time = now; new_qos->domain_info[id].ns_since_boot += time_since_update; new_qos->qdata[n].ns_gotten[id] += run_time; // if (domid == 0 && cpu == 1) // printf("adding run time for dom0 on cpu1\r\n"); } new_qos->domain_info[id].runnable_at_last_update = domain_runnable(domid); update_blocked_time(domid, now); // how much time passed since this datapoint was updated? if (now >= new_qos->qdata[n].timestamp) { // all is right with the world, time is increasing new_qos->qdata[n].ns_passed += (now - new_qos->qdata[n].timestamp); } else { // time wrapped around //new_qos->qdata[n].ns_passed += (now + (~0LL - new_qos->qdata[n].timestamp)); // printf("why timewrap?\r\n"); } new_qos->qdata[n].timestamp = now; } // called by dump routines to update all structures static void qos_update_all(uint64_t now, int cpu) { int i; for (i=0; idomain_info[i].in_use) qos_update_thread(cpu, new_qos->domain_info[i].id, now); } static void qos_update_thread_stats(int cpu, int domid, uint64_t now) { if (new_qos->qdata[new_qos->next_datapoint].ns_passed > (million*opts.ms_per_sample)) { qos_update_all(now, cpu); advance_next_datapoint(now); return; } qos_update_thread(cpu, domid, now); } // called when a new thread gets the cpu static void qos_switch_in(int cpu, int domid, uint64_t now, unsigned long ns_alloc, unsigned long ns_waited) { int idx = indexof(domid); new_qos->domain_info[idx].runnable = 1; update_blocked_time(domid, now); new_qos->domain_info[idx].blocked_start_time = 0; // invalidate new_qos->domain_info[idx].runnable_start_time = 0; // invalidate //runnable_start_time[idx] = 0; new_qos->domain_info[idx].start_time = now; new_qos->qdata[new_qos->next_datapoint].switchin_count[idx]++; new_qos->qdata[new_qos->next_datapoint].ns_allocated[idx] += ns_alloc; new_qos->qdata[new_qos->next_datapoint].ns_waiting[idx] += ns_waited; qos_update_thread_stats(cpu, domid, now); set_current(cpu, domid); // count up page flips for dom0 execution if (domid == 0) dom0_flips = 0; } // called when the current thread is taken off the cpu static void qos_switch_out(int cpu, int domid, uint64_t now, unsigned long gotten) { int idx = indexof(domid); int n; if (!is_current(domid, cpu)) { // printf("switching out domain %d but it is not current. gotten=%ld\r\n", id, gotten); } if (gotten == 0) { printf("gotten==0 in qos_switchout(domid=%d)\n", domid); } if (gotten < 100) { printf("gotten<100ns in qos_switchout(domid=%d)\n", domid); } n = new_qos->next_datapoint; #if 0 new_qos->qdata[n].ns_gotten[idx] += gotten; if (gotten > new_qos->qdata[n].ns_passed) printf("inconsistency #257, diff = %lld\n", gotten - new_qos->qdata[n].ns_passed ); #endif new_qos->domain_info[idx].ns_oncpu_since_boot += gotten; new_qos->domain_info[idx].runnable_start_time = now; // runnable_start_time[id] = now; qos_update_thread_stats(cpu, domid, now); // process dom0 page flips if (domid == 0) if (dom0_flips == 0) new_qos->qdata[n].flip_free_periods++; } // called when domain is put to sleep, may also be called // when thread is already asleep static void qos_state_sleeping(int cpu, int domid, uint64_t now) { int idx; if (!domain_runnable(domid)) // double call? return; idx = indexof(domid); new_qos->domain_info[idx].runnable = 0; new_qos->domain_info[idx].blocked_start_time = now; new_qos->domain_info[idx].runnable_start_time = 0; // invalidate // runnable_start_time[idx] = 0; // invalidate qos_update_thread_stats(cpu, domid, now); } // domain died, presume it's dead on all cpu's, not just mostly dead static void qos_kill_thread(int domid) { int cpu; for (cpu=0; cpudomain_info[indexof(domid)].in_use = 0; } } // called when thread becomes runnable, may also be called // when thread is already runnable static void qos_state_runnable(int cpu, int domid, uint64_t now) { int idx; qos_update_thread_stats(cpu, domid, now); if (domain_runnable(domid)) // double call? return; idx = indexof(domid); new_qos->domain_info[idx].runnable = 1; update_blocked_time(domid, now); new_qos->domain_info[idx].blocked_start_time = 0; /* invalidate */ new_qos->domain_info[idx].runnable_start_time = now; // runnable_start_time[id] = now; } static void qos_count_packets(domid_t domid, uint64_t now) { int i, idx = indexof(domid); _new_qos_data *cpu_data; for (i=0; idomain_info[idx].in_use) { cpu_data->qdata[cpu_data->next_datapoint].io_count[idx]++; } } new_qos->qdata[new_qos->next_datapoint].io_count[0]++; dom0_flips++; } static int process_record(int cpu, struct t_rec *r) { uint64_t now = 0; uint32_t *extra_u32 = r->u.nocycles.extra_u32; new_qos = cpu_qos_data[cpu]; rec_count++; if ( r->cycles_included ) { now = ((uint64_t)r->u.cycles.cycles_hi << 32) | r->u.cycles.cycles_lo; now = ((double)now) / (opts.cpu_freq / 1000.0); extra_u32 = r->u.cycles.extra_u32; } global_now = now; global_cpu = cpu; log_event(r->event); switch (r->event) { case TRC_SCHED_SWITCH_INFPREV: // domain data[0] just switched out and received data[1] ns of cpu time qos_switch_out(cpu, extra_u32[0], now, extra_u32[1]); // printf("ns_gotten %ld\n", extra_u32[1]); break; case TRC_SCHED_SWITCH_INFNEXT: // domain data[0] just switched in and // waited data[1] ns, and was allocated data[2] ns of cpu time qos_switch_in(cpu, extra_u32[0], now, extra_u32[2], extra_u32[1]); break; case TRC_SCHED_DOM_ADD: (void) indexof(extra_u32[0]); break; case TRC_SCHED_DOM_REM: qos_kill_thread(extra_u32[0]); break; case TRC_SCHED_SLEEP: qos_state_sleeping(cpu, extra_u32[0], now); break; case TRC_SCHED_WAKE: qos_state_runnable(cpu, extra_u32[0], now); break; case TRC_SCHED_BLOCK: qos_state_sleeping(cpu, extra_u32[0], now); break; case TRC_MEM_PAGE_GRANT_TRANSFER: qos_count_packets(extra_u32[0], now); break; default: break; } new_qos = NULL; return 4 + (r->cycles_included ? 8 : 0) + (r->extra_u32 * 4); }