HV:Acrn-hypvervisor Root Directory Clean-up and create misc/ folder for Acrn daemons, services and tools.

This patch is to clean-up acrn-hypervisor root directory, targt only 5 folders under acrn-hypervisor:1.hypervisor,2.devicemodel,3.misc,4.doc,5.build

Tracked-On: #3482
Signed-off-by: Terry Zou <terry.zou@intel.com>
Acked-by: Eddie Dong <eddie.dong@intel.com>
This commit is contained in:
Terry Zou
2019-07-29 12:21:54 +08:00
committed by Xie, Nanlin
parent 555a03db99
commit a9c38a5cfb
119 changed files with 62 additions and 57 deletions

View File

@@ -0,0 +1,50 @@
T := $(CURDIR)
OUT_DIR ?= $(shell mkdir -p $(T)/build;cd $(T)/build;pwd)
CC ?= gcc
TRACE_CFLAGS := -g -O0 -std=gnu11
TRACE_CFLAGS += -D_GNU_SOURCE
TRACE_CFLAGS += -DNO_OPENSSL
TRACE_CFLAGS += -m64
TRACE_CFLAGS += -Wall -ffunction-sections
TRACE_CFLAGS += -Werror
TRACE_CFLAGS += -O2 -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=2
TRACE_CFLAGS += -Wformat -Wformat-security -fno-strict-aliasing
TRACE_CFLAGS += -fpie -fpic
TRACE_CFLAGS += $(CFLAGS)
GCC_MAJOR=$(shell echo __GNUC__ | $(CC) -E -x c - | tail -n 1)
GCC_MINOR=$(shell echo __GNUC_MINOR__ | $(CC) -E -x c - | tail -n 1)
#enable stack overflow check
STACK_PROTECTOR := 1
ifdef STACK_PROTECTOR
ifeq (true, $(shell [ $(GCC_MAJOR) -gt 4 ] && echo true))
TRACE_CFLAGS += -fstack-protector-strong
else
ifeq (true, $(shell [ $(GCC_MAJOR) -eq 4 ] && [ $(GCC_MINOR) -ge 9 ] && echo true))
TRACE_CFLAGS += -fstack-protector-strong
else
TRACE_CFLAGS += -fstack-protector
endif
endif
endif
TRACE_LDFLAGS := -Wl,-z,noexecstack
TRACE_LDFLAGS += -Wl,-z,relro,-z,now
TRACE_LDFLAGS += -pie
TRACE_LDFLAGS += $(LDFLAGS)
all:
$(CC) -o $(OUT_DIR)/acrntrace acrntrace.c sbuf.c -I. -lpthread -lrt $(TRACE_CFLAGS) $(TRACE_LDFLAGS)
clean:
rm -f $(OUT_DIR)/acrntrace
ifneq ($(OUT_DIR),.)
rm -rf $(OUT_DIR)
endif
install: $(OUT_DIR)/acrntrace
install -d $(DESTDIR)/usr/bin
install -t $(DESTDIR)/usr/bin $(OUT_DIR)/acrntrace

View File

@@ -0,0 +1,171 @@
.. _acrntrace:
acrntrace
#########
Description
***********
``acrntrace`` is a tool running on the Service OS (SOS) to capture trace data.
A ``scripts`` directory includes scripts to analyze the trace data.
Usage
*****
acrntrace
=========
The ``acrntrace`` tool runs on the Service OS (SOS) to capture trace data and
output to trace file under ``./acrntrace`` with raw (binary) data format.
Options:
-h print this message
-i period specify polling interval in milliseconds [1-999]
-t max_time max time to capture trace data (in second)
-c clear the buffered old data
acrntrace_format.py
===================
The ``acrntrace_format.py`` is a offline tool for parsing trace data (as output
by acrntrace) to human-readable formats based on given format.
Here's an explanation of the tool's parameters:
.. code-block:: none
acrntrace_format.py [options] [formats] [trace_data]
Options:
-h print this message
*formats* file specifies the rules to reformat the *trace_data* collected by
``acrntrace`` into a human-readable text form. The rules in this file are of
the form::
event_id text_format_string
The text_format_string may include format specifiers, such as
``%(cpu)d``, ``%(tsc)d``, ``%(event)d``, ``%(1)d``, and ``%(2)d``.
The 'd' format specifier outputs in decimal, alternatively 'x' will
output in hexadecimal and 'o' will output in octal.
These respectively correspond to the CPU number (cpu), timestamp
counter (tsc), event ID (event) and the data logged in the trace file.
There can be only one such rule for each type of event.
An example *formats_file* is available in the acrn_hypervisor repo in
``hypervisor/tools/acrntrace/scripts/formats``.
acrnalyze.py
============
The ``acrnalyze.py`` is a offline tool to analyze trace data (as output by
acrntrace) based on given analyzer, such as ``vm_exit`` or ``irq``.
Options:
.. list-table::
* - :kbd:`-h`
- print this message
* - :kbd:`-i, --ifile=string`
- input file name
* - :kbd:`-o, --ofile=string`
- output filename
* - :kbd:`-f, --frequency=unsigned_int`
- TSC frequency in MHz
* - :kbd:`--vm_exit`
- generate a vm_exit report
* - :kbd:`--irq`
- generate an IRQ-related report
.. note:: We depend on TSC frequency to do time-based analysis. Please configure
the right TSC frequency that acrn runs on. TSC frequency can be obtained
from the ACRN console log (calibrate_tsc, tsc_hz=xxx) when the hypervisor boots.
The tool does not take into account CPU frequency variation that can
occur during normal operation (aka CPU throttling) on the processor which
doesn't support for invariant TSC. The results may therefore not be
completely accurate in that regard.
Typical use example
===================
Here's a typical use of ``acrntrace`` to capture trace data from the SOS,
converting the binary data to human-readable form, copying the processed trace
data to your linux system, and running the analysis tool.
1. On the SOS, clear buffers before starting a trace, with:
.. code-block:: none
# acrntrace -c
#. Start capturing buffered trace data with:
.. code-block:: none
# acrntrace
Trace files are created under current directory where we launch acrntrace,
with a date-time-based directory name such as ``./acrntrace/20171115-101605``
#. When done, stop a running ``acrntrace``, with:
.. code-block:: none
q <enter>
#. Convert trace data to human-readable format, with:
.. code-block:: none
# acrntrace_format.py formats trace_data
Trace data will be converted to human-readable format based on given format
and printed to stdout.
#. Analysis of the collected data is done on a Linux PC, so you'll need
to copy the collected trace data to your Linux system (using ``scp`` is
recommended):
.. code-block:: none
# scp -r ./acrntrace/20171115-101605/ \
username@hostname:/home/username/trace_data
Replace username and hostname with appropriate values.
#. On the Linux system, run the provided Python3 script to analyze the
``vm_exits``, ``irq``:
.. code-block:: none
# acrnalyze.py -i /home/xxxx/trace_data/20171115-101605/0 \
-o /home/xxxx/trace_data/20171115-101605/cpu0 --vm_exit --irq
- Analysis report is written to stdout, or to a CSV file if
a filename is specified using ``-o filename``.
- The scripts require Python3.
Build and Install
*****************
The source files for ``acrntrace`` are in the ``tools/acrntrace`` folder,
and can be built and installed using:
.. code-block:: none
# make
# make install
The processing scripts are in ``tools/acrntrace/scripts`` and need to be
copied to and run on your Linux system.

View File

@@ -0,0 +1,390 @@
/*
* Copyright (C) 2018 Intel Corporation. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/statvfs.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <time.h>
#include <dirent.h>
#include <signal.h>
#include <errno.h>
#include <pthread.h>
#include <string.h>
#include <signal.h>
#include "acrntrace.h"
#define TIMER_ID (128)
static uint32_t timeout = 0;
static int exiting = 0;
/* for opt */
static uint64_t period = 10000;
static const char optString[] = "i:hct:";
static const char dev_prefix[] = "acrn_trace_";
static uint32_t flags;
static char trace_file_dir[TRACE_FILE_DIR_LEN];
static reader_struct *reader;
static int dev_cnt = 0; /* Count of /dev/acrn_trace_xxx devices */
static void display_usage(void)
{
printf("acrntrace - tool to collect ACRN trace data\n"
"[Usage] acrntrace [-i period] [-t max_time] [-ch]\n\n"
"[Options]\n"
"\t-h: print this message\n"
"\t-i: period_in_ms: specify polling interval [1-999]\n"
"\t-t: max time to capture trace data (in second)\n"
"\t-c: clear the buffered old data\n");
}
static void timer_handler(union sigval sv)
{
exiting = 1;
exit(0);
}
static int init_timer(int timeout)
{
struct sigevent event;
struct itimerspec it;
timer_t tm_id;
int err;
memset(&event, 0, sizeof(struct sigevent));
event.sigev_value.sival_int = TIMER_ID;
event.sigev_notify = SIGEV_THREAD;
event.sigev_notify_function = timer_handler;
err = timer_create(CLOCK_MONOTONIC, &event, &tm_id);
if (err < 0) {
pr_err("Error to create timer, errno(%d)\n", err);
return err;
}
it.it_interval.tv_sec = timeout;
it.it_interval.tv_nsec = 0;
it.it_value.tv_sec = timeout;
it.it_value.tv_nsec = 0;
err = timer_settime(tm_id, 0, &it, NULL);
if (err < 0) {
pr_err("Error to set timer, errno(%d)\n", err);
return err;
}
pr_info("Capture trace data for about %ds and exit\n", timeout);
return 0;
}
static int parse_opt(int argc, char *argv[])
{
int opt, ret;
while ((opt = getopt(argc, argv, optString)) != -1) {
switch (opt) {
case 'i':
ret = strtol(optarg, NULL, 10);
if (ret <= 0 || ret >=1000) {
pr_err("'-i' require integer between [1-999]\n");
return -EINVAL;
}
period = ret * 1000;
pr_dbg("Period is %lu\n", period);
break;
case 't':
ret = strtol(optarg, NULL, 10);
if (ret <= 0) {
pr_err("'-t' require integer greater than 0\n");
return -EINVAL;
}
timeout = ret;
pr_dbg("Capture trace data for at most %ds\n", ret);
break;
case 'c':
flags |= FLAG_CLEAR_BUF;
break;
case 'h':
display_usage();
return -EINVAL;
default:
/* Undefined operation. */
display_usage();
return -EINVAL;
}
};
return 0;
}
static int get_dev_cnt(void)
{
struct dirent *pdir;
int cnt = 0;
char *ret;
DIR *dir;
dir = opendir("/dev");
if (!dir) {
printf("Error opening /dev: %s\n", strerror(errno));
return -1;
}
while ((pdir = readdir(dir)) != NULL) {
ret = strstr(pdir->d_name, dev_prefix);
if (ret)
cnt++;
}
closedir(dir);
return cnt;
}
static int create_trace_file_dir(char *dir)
{
int err = 0, ret;
char time_str[TIME_STR_LEN + 1];
time_t timep;
struct tm *p;
struct stat st;
time(&timep);
p = localtime(&timep);
if (p) {
ret = snprintf(time_str, TIME_STR_LEN + 1,
"%04d%02d%02d-%02d%02d%02d",
(1900 + p->tm_year), (1 + p->tm_mon),
p->tm_mday, p->tm_hour, p->tm_min, p->tm_sec);
if (ret > TIME_STR_LEN)
printf("WARN: time_str may be truncated\n");
} else {
if (snprintf(time_str, TIME_STR_LEN, "00000000-000000") >= TIME_STR_LEN)
printf("WARN: time_str is truncated\n\n");
}
pr_info("start tracing at %s\n", time_str);
if (stat(TRACE_FILE_ROOT, &st)) {
err = mkdir(TRACE_FILE_ROOT, 0644);
if (err) {
pr_err("Fail to create dir %s, Error: %s\n",
TRACE_FILE_ROOT, strerror(errno));
return -1;
}
}
ret = snprintf(dir, TRACE_FILE_DIR_LEN, "%s%s",
TRACE_FILE_ROOT, time_str);
if (ret >= TRACE_FILE_DIR_LEN)
printf("WARN: trace file dir name is truncated\n");
if (stat(dir, &st)) {
err = mkdir(dir, 0644);
if (err) {
pr_err("Fail to create dir %s, Error: %s\n",
dir, strerror(errno));
return -1;
}
}
pr_dbg("dir %s creted\n", dir);
return err;
}
/* function executed in each consumer thread */
static void reader_fn(param_t * param)
{
int ret;
int fd = param->trace_fd;
shared_buf_t *sbuf = param->sbuf;
pr_dbg("reader thread[%lu] created for FILE*[0x%p]\n",
pthread_self(), fp);
pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL);
/* Clear the old data in sbuf */
if (flags & FLAG_CLEAR_BUF)
sbuf_clear_buffered(sbuf);
while (1) {
do {
ret = sbuf_write(fd, sbuf);
} while (ret > 0);
usleep(period);
}
}
static int create_reader(reader_struct * reader, uint32_t dev_id)
{
char trace_file_name[TRACE_FILE_NAME_LEN];
if (snprintf(reader->dev_name, DEV_PATH_LEN, "/dev/%s%u", dev_prefix, dev_id)
>= DEV_PATH_LEN)
printf("WARN: device name is truncated\n");
reader->param.devid = dev_id;
reader->dev_fd = open(reader->dev_name, O_RDWR);
if (reader->dev_fd < 0) {
pr_err("Failed to open %s, err %d\n", reader->dev_name, errno);
reader->dev_fd = 0;
return -1;
}
reader->param.sbuf = mmap(NULL, MMAP_SIZE,
PROT_READ | PROT_WRITE,
MAP_SHARED, reader->dev_fd, 0);
if (reader->param.sbuf == MAP_FAILED) {
pr_err("mmap failed for %s, errno %d\n", reader->dev_name, errno);
reader->param.sbuf = NULL;
return -2;
}
pr_dbg("sbuf[%d]:\nmagic_num: %lx\nele_num: %u\n ele_size: %u\n",
dev_id, reader->param.sbuf->magic, reader->param.sbuf->ele_num,
reader->param.sbuf->ele_size);
if(snprintf(trace_file_name, TRACE_FILE_NAME_LEN, "%s/%d", trace_file_dir,
dev_id) >= TRACE_FILE_NAME_LEN)
printf("WARN: trace file name is truncated\n");
reader->param.trace_fd = open(trace_file_name,
O_WRONLY | O_CREAT | O_TRUNC, 0644);
if (!reader->param.trace_fd) {
pr_err("Failed to open %s, err %d\n", trace_file_name, errno);
return -3;
}
pr_info("trace data file %s created for %s\n",
trace_file_name, reader->dev_name);
if (pthread_create(&reader->thrd, NULL,
(void *)&reader_fn, &reader->param)) {
pr_err("failed to create reader thread, %d\n", dev_id);
return -4;
}
return 0;
}
static void destory_reader(reader_struct * reader)
{
if (reader->thrd) {
pthread_cancel(reader->thrd);
if (pthread_join(reader->thrd, NULL) != 0)
pr_err("failed to cancel thread[%lu]\n", reader->thrd);
else
reader->thrd = 0;
}
if (reader->param.sbuf) {
munmap(reader->param.sbuf, MMAP_SIZE);
reader->param.sbuf = NULL;
}
if (reader->dev_fd) {
close(reader->dev_fd);
reader->dev_fd = 0;
}
if (reader->param.trace_fd) {
close(reader->param.trace_fd);
}
}
static void handle_on_exit(void)
{
uint32_t dev_id;
/* if nothing to release */
if (!(flags & FLAG_TO_REL))
return;
pr_info("exiting - to release resources...\n");
foreach_dev(dev_id)
destory_reader(&reader[dev_id]);
}
static void signal_exit_handler(int sig)
{
pr_info("exit on signal %d\n", sig);
exit(0);
}
int main(int argc, char *argv[])
{
uint32_t dev_id = 0;
int err;
/* parse options */
if (parse_opt(argc, argv))
exit(EXIT_FAILURE);
dev_cnt = get_dev_cnt();
if (dev_cnt == 0) {
pr_err("Failed to find acrn trace devices, please check whether module acrn_trace is inserted\n");
exit(EXIT_FAILURE);
}
reader = calloc(1, sizeof(reader_struct) * dev_cnt);
if (!reader) {
pr_err("Failed to allocate reader memory\n");
exit(EXIT_FAILURE);
}
/* create dir for trace file */
if (create_trace_file_dir(trace_file_dir)) {
pr_err("Failed to create dir for trace files\n");
exit(EXIT_FAILURE);
}
/* Set timer if timeout configured */
if (timeout) {
err = init_timer(timeout);
if (err < 0) {
pr_err("Failed to set timer\n");
exit(EXIT_FAILURE);
}
}
atexit(handle_on_exit);
/* acquair res for each trace dev */
flags |= FLAG_TO_REL;
foreach_dev(dev_id)
if (create_reader(&reader[dev_id], dev_id) < 0)
goto out_free;
/* for kill exit handling */
signal(SIGTERM, signal_exit_handler);
signal(SIGINT, signal_exit_handler);
/* wait for user input to stop */
printf("q <enter> to quit:\n");
while (!exiting && getchar() != 'q')
printf("q <enter> to quit:\n");
out_free:
foreach_dev(dev_id)
destory_reader(&reader[dev_id]);
free(reader);
flags &= ~FLAG_TO_REL;
return EXIT_SUCCESS;
}

View File

@@ -0,0 +1,86 @@
/*
* Copyright (C) 2018 Intel Corporation. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include "sbuf.h"
#define PCPU_NUM 4
#define TRACE_ELEMENT_SIZE 32 /* byte */
#define TRACE_ELEMENT_NUM ((4 * 1024 * 1024 - 64) / TRACE_ELEMENT_SIZE)
#define PAGE_SIZE 4096
#define PAGE_MASK (~(PAGE_SIZE - 1))
#define MMAP_SIZE (4 * 1024 * 1024)
/*
#define MMAP_SIZE ((TRACE_ELEMENT_SIZE * TRACE_ELEMENT_NUM \
+ PAGE_SIZE - 1) & PAGE_MASK)
*/
#define TRACE_FILE_NAME_LEN 32
#define TRACE_FILE_DIR_LEN (TRACE_FILE_NAME_LEN - 3)
#define TRACE_FILE_ROOT "acrntrace/"
#define DEV_PATH_LEN 18
#define TIME_STR_LEN 16
#define CMD_MAX_LEN 48
#define pr_fmt(fmt) "acrntrace: " fmt
#define pr_info(fmt, ...) printf(pr_fmt(fmt), ##__VA_ARGS__)
#define pr_err(fmt, ...) printf(pr_fmt(fmt), ##__VA_ARGS__)
#ifdef DEBUG
#define pr_dbg(fmt, ...) printf(pr_fmt(fmt), ##__VA_ARGS__)
#else
#define pr_dbg(fmt, ...)
#endif
/*
* flags:
* FLAG_TO_REL - resources need to be release
* FLAG_CLEAR_BUF - to clear buffered old data
*/
#define FLAG_TO_REL (1UL << 0)
#define FLAG_CLEAR_BUF (1UL << 1)
#define foreach_dev(dev_id) \
for ((dev_id) = 0; (dev_id) < (dev_cnt); (dev_id)++)
typedef unsigned char uint8_t;
typedef unsigned int uint32_t;
typedef unsigned long uint64_t;
typedef struct {
uint64_t tsc;
uint64_t id;
union {
struct {
uint32_t a, b, c, d;
};
struct {
uint8_t a1, a2, a3, a4;
uint8_t b1, b2, b3, b4;
uint8_t c1, c2, c3, c4;
uint8_t d1, d2, d3, d4;
};
struct {
uint64_t e;
uint64_t f;
};
char str[16];
};
} trace_ev_t;
typedef struct {
uint32_t devid;
int exit_flag;
int trace_fd;
shared_buf_t *sbuf;
pthread_mutex_t *sbuf_lock;
} param_t;
typedef struct {
int dev_fd;
char dev_name[DEV_PATH_LEN];
pthread_t thrd;
param_t param;
} reader_struct;

View File

@@ -0,0 +1,82 @@
/*
* Copyright (C) 2018 Intel Corporation. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <asm/errno.h>
#include <string.h>
#include <unistd.h>
#include <stdio.h>
#include <stdbool.h>
#include "sbuf.h"
#include <errno.h>
static inline bool sbuf_is_empty(shared_buf_t *sbuf)
{
return (sbuf->head == sbuf->tail);
}
static inline uint32_t sbuf_next_ptr(uint32_t pos,
uint32_t span, uint32_t scope)
{
pos += span;
pos = (pos >= scope) ? (pos - scope) : pos;
return pos;
}
int sbuf_get(shared_buf_t *sbuf, uint8_t *data)
{
const void *from;
if ((sbuf == NULL) || (data == NULL))
return -EINVAL;
if (sbuf_is_empty(sbuf)) {
/* no data available */
return 0;
}
from = (void *)sbuf + SBUF_HEAD_SIZE + sbuf->head;
memcpy(data, from, sbuf->ele_size);
sbuf->head = sbuf_next_ptr(sbuf->head, sbuf->ele_size, sbuf->size);
return sbuf->ele_size;
}
int sbuf_write(int fd, shared_buf_t *sbuf)
{
const void *start;
int written;
if (sbuf == NULL)
return -EINVAL;
if (sbuf_is_empty(sbuf)) {
return 0;
}
start = (void *)sbuf + SBUF_HEAD_SIZE + sbuf->head;
written = write(fd, start, sbuf->ele_size);
if (written != sbuf->ele_size) {
printf("Failed to write: ret %d (ele_size %d), errno %d\n",
written, sbuf->ele_size, (written == -1) ? errno : 0);
return -1;
}
sbuf->head = sbuf_next_ptr(sbuf->head, sbuf->ele_size, sbuf->size);
return sbuf->ele_size;
}
int sbuf_clear_buffered(shared_buf_t *sbuf)
{
if (sbuf == NULL)
return -EINVAL;
sbuf->head = sbuf->tail;
return 0;
}

View File

@@ -0,0 +1,71 @@
/*
* Copyright (C) 2018 Intel Corporation. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#ifndef SHARED_BUF_H
#define SHARED_BUF_H
#include <linux/types.h>
#define SBUF_MAGIC 0x5aa57aa71aa13aa3
#define SBUF_MAX_SIZE (1ULL << 22)
#define SBUF_HEAD_SIZE 64
/* sbuf flags */
#define OVERRUN_CNT_EN (1ULL << 0) /* whether overrun counting is enabled */
#define OVERWRITE_EN (1ULL << 1) /* whether overwrite is enabled */
typedef unsigned char uint8_t;
typedef unsigned int uint32_t;
typedef unsigned long uint64_t;
/**
* (sbuf) head + buf (store (ele_num - 1) elements at most)
* buffer empty: tail == head
* buffer full: (tail + ele_size) % size == head
*
* Base of memory for elements
* |
* |
* ---------------------------------------------------------------------------------------
* | shared_buf_t | raw data (ele_size)| raw date (ele_size) | ... | raw data (ele_size) |
* ---------------------------------------------------------------------------------------
* |
* |
* shared_buf_t *buf
*/
/* Make sure sizeof(shared_buf_t) == SBUF_HEAD_SIZE */
typedef struct shared_buf {
uint64_t magic;
uint32_t ele_num; /* number of elements */
uint32_t ele_size; /* sizeof of elements */
uint32_t head; /* offset from base, to read */
uint32_t tail; /* offset from base, to write */
uint64_t flags;
uint32_t overrun_cnt; /* count of overrun */
uint32_t size; /* ele_num * ele_size */
uint32_t padding[6];
} shared_buf_t;
static inline void sbuf_clear_flags(shared_buf_t *sbuf, uint64_t flags)
{
sbuf->flags &= ~flags;
}
static inline void sbuf_set_flags(shared_buf_t *sbuf, uint64_t flags)
{
sbuf->flags = flags;
}
static inline void sbuf_add_flags(shared_buf_t *sbuf, uint64_t flags)
{
sbuf->flags |= flags;
}
int sbuf_get(shared_buf_t *sbuf, uint8_t *data);
int sbuf_write(int fd, shared_buf_t *sbuf);
int sbuf_clear_buffered(shared_buf_t *sbuf);
#endif /* SHARED_BUF_H */

View File

@@ -0,0 +1,98 @@
#!/usr/bin/python3
# -*- coding: UTF-8 -*-
"""
This is the main script of arnalyzer, which:
- parse the options
- call a specific script to do analysis
"""
import sys
import getopt
import os
from vmexit_analyze import analyze_vm_exit
from irq_analyze import analyze_irq
def usage():
"""print the usage of the script
Args: NA
Returns: None
Raises: NA
"""
print ('''
[Usage] acrnalyze.py [options] [value] ...
[options]
-h: print this message
-i, --ifile=[string]: input file
-o, --ofile=[string]: output file
-f, --frequency=[unsigned int]: TSC frequency in MHz
--vm_exit: to generate vm_exit report
--irq: to generate irq related report
''')
def do_analysis(ifile, ofile, analyzer, freq):
"""do the specific analysis
Args:
ifile: input trace data file
ofile: output analysis report file
analyzer: a function do the specific analysis
freq: TSC frequency of the host where we capture the trace data
Returns:
None
Raises:
NA
"""
for alyer in analyzer:
alyer(ifile, ofile, freq)
def main(argv):
"""Main enterance function
Args:
argv: arguments string
Returns:
None
Raises:
GetoptError
"""
inputfile = ''
outputfile = ''
# Default TSC frequency of MRB in MHz
freq = 1881.6
opts_short = "hi:o:f:"
opts_long = ["ifile=", "ofile=", "frequency=", "vm_exit", "irq"]
analyzer = []
try:
opts, args = getopt.getopt(argv, opts_short, opts_long)
except getopt.GetoptError:
usage()
sys.exit(1)
for opt, arg in opts:
if opt == '-h':
usage()
sys.exit()
elif opt in ("-i", "--ifile"):
inputfile = arg
elif opt in ("-o", "--ofile"):
outputfile = arg
elif opt in ("-f", "--frequency"):
freq = arg
elif opt == "--vm_exit":
analyzer.append(analyze_vm_exit)
elif opt == "--irq":
analyzer.append(analyze_irq)
else:
assert False, "unhandled option"
assert inputfile != '', "input file is required"
assert outputfile != '', "output file is required"
assert analyzer != '', 'MUST contain one of analyzer: ''vm_exit'
do_analysis(inputfile, outputfile, analyzer, freq)
if __name__ == "__main__":
main(sys.argv[1:])

View File

@@ -0,0 +1,197 @@
#!/usr/bin/python3
# -*- coding: UTF-8 -*-
import os
import re
import sys
import string
import signal
import struct
import getopt
def usage():
print >> sys.stderr, \
"""
Usage:
acrntrace_format.py [options] [formats] [trace_data]
[options]
-h: print this message
Parses trace_data in binary format generated by acrntrace and
reformats it according to the rules in the [formats] file.
The rules in formats should have the format ({ and } show grouping
and are not part of the syntax):
{event_id}{whitespace}{text format string}
The textual format string may include format specifiers, such as
%(cpu)d, %(tsc)d, %(event)d, %(1)d, %(2)d, ....
The 'd' format specifier outputs in decimal, alternatively 'x' will
output in hexadecimal and 'o' will output in octal.
These respectively correspond to the CPU number (cpu), timestamp
counter (tsc), event ID (event) and the data logged in the trace file.
There can be only one such rule for each type of event.
"""
def read_format(format_file):
formats = {}
fd = open(format_file)
reg = re.compile('(\S+)\s+(\S.*)')
while True:
line = fd.readline()
if not line:
break
if line[0] == '#' or line[0] == '\n':
continue
m = reg.match(line)
if not m: print >> sys.stderr, "Wrong format file"; sys.exit(1)
formats[str(eval(m.group(1)))] = m.group(2)
return formats
exit = 0
# structure of trace data (as output by acrntrace)
# TSC(Q) HDR(Q) D1 D2 ...
# HDR consists of event:48:, n_data:8:, cpu:8:
# event means Event ID
# n_data means number of data in trace entry (like D1, D2, ...)
# cpu means cpu id this trace entry belong to
TSCREC = "Q"
HDRREC = "Q"
D2REC = "QQ"
D4REC = "IIII"
D8REC = "BBBBBBBBBBBBBBBB"
D16REC = "bbbbbbbbbbbbbbbb"
def main_loop(formats, fd):
global exit
i = 0
while not exit:
try:
i = i + 1
line = fd.read(struct.calcsize(TSCREC))
if not line:
break
tsc = struct.unpack(TSCREC, line)[0]
line = fd.read(struct.calcsize(HDRREC))
if not line:
break
event = struct.unpack(HDRREC, line)[0]
n_data = event >> 48 & 0xff
cpu = event >> 56
event = event & 0xffffffffffff
d1 = 0
d2 = 0
d3 = 0
d4 = 0
d5 = 0
d6 = 0
d7 = 0
d8 = 0
d9 = 0
d10 = 0
d11 = 0
d12 = 0
d13 = 0
d14 = 0
d15 = 0
d16 = 0
if n_data == 2:
line = fd.read(struct.calcsize(D2REC))
if not line:
break
(d1, d2) = struct.unpack(D2REC, line)
if n_data == 4:
line = fd.read(struct.calcsize(D4REC))
if not line:
break
(d1, d2, d3, d4) = struct.unpack(D4REC, line)
if n_data == 8:
line = fd.read(struct.calcsize(D8REC))
if not line:
break
# TRACE_6C using the first 6 data of fields_8. Actaully we have
# 16 data in every trace entry.
(d1, d2, d3, d4, d5, d6, d7, d8,
d9, d10, d11, d12, d13, d14, d15, d16) = struct.unpack(D8REC, line)
if n_data == 16:
line = fd.read(struct.calcsize(D16REC))
if not line:
break
(d1, d2, d3, d4, d5, d6, d7, d8,
d9, d10, d11, d12, d13, d14, d15, d16) = struct.unpack(D16REC, line)
args = {'cpu' : cpu,
'tsc' : tsc,
'event' : event,
'1' : d1,
'2' : d2,
'3' : d3,
'4' : d4,
'5' : d5,
'6' : d6,
'7' : d7,
'8' : d8,
'9' : d9,
'10' : d10,
'11' : d11,
'12' : d12,
'13' : d13,
'14' : d14,
'15' : d15,
'16' : d16 }
try:
if str(event) in formats.keys():
print (formats[str(event)] % args)
except TypeError:
if str(event) in formats.key():
print (formats[str(event)])
print (args)
except struct.error:
sys.exit()
def main(argv):
try:
opts, arg = getopt.getopt(sys.argv[1:], "h")
for opt in opts:
if opt[0] == '-h':
usage()
sys.exit()
except getopt.GetoptError:
usage()
sys.exit(1)
try:
formats = read_format(arg[0])
fd = open(arg[1], 'rb')
except IOError:
sys.exit(1)
main_loop(formats, fd)
if __name__ == "__main__":
main(sys.argv[1:])

View File

@@ -0,0 +1,22 @@
# For TRACE_2L
0x00000001 CPU%(cpu)d 0x%(event)016x %(tsc)d timer added [fire_tsc = 0x%(1)08x]
0x00000002 CPU%(cpu)d 0x%(event)016x %(tsc)d timer pickup [fire tsc = 0x%(1)08x]
0x00000010 CPU%(cpu)d 0x%(event)016x %(tsc)d vmexit [exit reason = 0x%(1)08x, rIP = 0x%(2)08x]
0x00000011 CPU%(cpu)d 0x%(event)016x %(tsc)d vmenter
0x00010001 CPU%(cpu)d 0x%(event)016x %(tsc)d external intr [vector = 0x%(1)08x]
0x00010002 CPU%(cpu)d 0x%(event)016x %(tsc)d intr window
0x00010004 CPU%(cpu)d 0x%(event)016x %(tsc)d cpuid [vcpuid = %(1)d]
0x00010012 CPU%(cpu)d 0x%(event)016x %(tsc)d hypercall [vmid = %(1)d, hypercall id = 0x%(2)08x]
0x0001001C CPU%(cpu)d 0x%(event)016x %(tsc)d cr access [access type = 0x%(1)08x, cr num = %(2)d]
0x0001001F CPU%(cpu)d 0x%(event)016x %(tsc)d read msr [msr = 0x%(1)08x, val = 0x%(2)016x]
0x00010020 CPU%(cpu)d 0x%(event)016x %(tsc)d write msr [msr = 0x%(1)08x, val = 0x%(2)016x]
0x00010030 CPU%(cpu)d 0x%(event)016x %(tsc)d ept violation [exit qual = 0x%(1)08x, gpa = 0x%(2)08x]
0x00010031 CPU%(cpu)d 0x%(event)016x %(tsc)d ept misconfiguration
0x00010038 CPU%(cpu)d 0x%(event)016x %(tsc)d apicv write [offset = 0x%(1)08x]
0x00010039 CPU%(cpu)d 0x%(event)016x %(tsc)d apicv access [qual = 0x%(1)08x, vlapic = 0x%(2)08x]
0x0001003A CPU%(cpu)d 0x%(event)016x %(tsc)d apicv virt EOI [vector = 0x%(1)08x]
0x00020000 CPU%(cpu)d 0x%(event)016x %(tsc)d vmexit unhandled [exit reason = 0x%(1)08x]
# For TRACE_4I
0x0001001E CPU%(cpu)d 0x%(event)016x %(tsc)d IO instruction [port = %(1)d, direction = %(2)d, sz = %(3)d, cur_context_idx = %(4)d]
0x00010000 CPU%(cpu)d 0x%(event)016x %(tsc)d exception or nmi [vector = 0x%(1)08x, err = %(2)d, d3 = %(1)d, d4 = %(2)d]

View File

@@ -0,0 +1,107 @@
#!/usr/bin/python3
# -*- coding: UTF-8 -*-
"""
This script defines the function to do the irq related analysis
"""
import csv
import struct
TSC_BEGIN = 0
TSC_END = 0
VMEXIT_ENTRY = 0x10000
LIST_EVENTS = {
'VMEXIT_EXTERNAL_INTERRUPT': VMEXIT_ENTRY + 0x00000001,
}
IRQ_EXITS = {}
# 4 * 64bit per trace entry
TRCREC = "QQQQ"
def parse_trace(ifile):
"""parse the trace data file
Args:
ifile: input trace data file
Return:
None
"""
fd = open(ifile, 'rb')
while True:
global TSC_BEGIN, TSC_END
try:
line = fd.read(struct.calcsize(TRCREC))
if not line:
break
(tsc, event, vec, d2) = struct.unpack(TRCREC, line)
event = event & 0xffffffffffff
if TSC_BEGIN == 0:
TSC_BEGIN = tsc
TSC_END = tsc
for key in LIST_EVENTS.keys():
if event == LIST_EVENTS.get(key):
if vec in IRQ_EXITS.keys():
IRQ_EXITS[vec] += 1
else:
IRQ_EXITS[vec] = 1
except struct.error:
sys.exit()
def generate_report(ofile, freq):
""" generate analysis report
Args:
ofile: output report
freq: TSC frequency of the device trace data from
Return:
None
"""
global TSC_BEGIN, TSC_END
csv_name = ofile + '.csv'
try:
with open(csv_name, 'a') as filep:
f_csv = csv.writer(filep)
rt_cycle = TSC_END - TSC_BEGIN
assert rt_cycle != 0, "Total run time in cycle is 0, \
TSC end %d, TSC begin %d" \
% (TSC_END, TSC_BEGIN)
rt_sec = float(rt_cycle) / (float(freq) * 1000 * 1000)
print ("%-8s\t%-8s\t%-8s" % ("Vector", "Count", "NR_Exit/Sec"))
f_csv.writerow(['Vector', 'NR_Exit', 'NR_Exit/Sec'])
for e in IRQ_EXITS.keys():
pct = float(IRQ_EXITS[e]) / rt_sec
print ("0x%08x\t%-8d\t%-8.2f" % (e, IRQ_EXITS[e], pct))
f_csv.writerow(['0x%08x' % e, IRQ_EXITS[e], '%.2f' % pct])
except IOError as err:
print ("Output File Error: " + str(err))
def analyze_irq(ifile, ofile, freq):
"""do the vm exits analysis
Args:
ifile: input trace data file
ofile: output report file
freq: TSC frequency of the host where we capture the trace data
Return:
None
"""
print("IRQ analysis started... \n\tinput file: %s\n"
"\toutput file: %s.csv" % (ifile, ofile))
parse_trace(ifile)
# save report to the output file
generate_report(ofile, freq)

View File

@@ -0,0 +1,217 @@
#!/usr/bin/python3
# -*- coding: UTF-8 -*-
"""
This script defines the function to do the vm_exit analysis
"""
import csv
import struct
TSC_BEGIN = 0
TSC_END = 0
TOTAL_NR_EXITS = 0
VM_EXIT = 0x10
VM_ENTER = 0x11
VMEXIT_ENTRY = 0x10000
LIST_EVENTS = {
'VMEXIT_EXCEPTION_OR_NMI': VMEXIT_ENTRY + 0x00000000,
'VMEXIT_EXTERNAL_INTERRUPT': VMEXIT_ENTRY + 0x00000001,
'VMEXIT_INTERRUPT_WINDOW': VMEXIT_ENTRY + 0x00000002,
'VMEXIT_CPUID': VMEXIT_ENTRY + 0x00000004,
'VMEXIT_RDTSC': VMEXIT_ENTRY + 0x00000010,
'VMEXIT_VMCALL': VMEXIT_ENTRY + 0x00000012,
'VMEXIT_CR_ACCESS': VMEXIT_ENTRY + 0x0000001C,
'VMEXIT_IO_INSTRUCTION': VMEXIT_ENTRY + 0x0000001E,
'VMEXIT_RDMSR': VMEXIT_ENTRY + 0x0000001F,
'VMEXIT_WRMSR': VMEXIT_ENTRY + 0x00000020,
'VMEXIT_EPT_VIOLATION': VMEXIT_ENTRY + 0x00000030,
'VMEXIT_EPT_MISCONFIGURATION': VMEXIT_ENTRY + 0x00000031,
'VMEXIT_RDTSCP': VMEXIT_ENTRY + 0x00000033,
'VMEXIT_APICV_WRITE': VMEXIT_ENTRY + 0x00000038,
'VMEXIT_APICV_ACCESS': VMEXIT_ENTRY + 0x00000039,
'VMEXIT_APICV_VIRT_EOI': VMEXIT_ENTRY + 0x0000003A,
'VMEXIT_UNHANDLED': 0x20000
}
NR_EXITS = {
'VMEXIT_EXCEPTION_OR_NMI': 0,
'VMEXIT_EXTERNAL_INTERRUPT': 0,
'VMEXIT_INTERRUPT_WINDOW': 0,
'VMEXIT_CPUID': 0,
'VMEXIT_RDTSC': 0,
'VMEXIT_VMCALL': 0,
'VMEXIT_CR_ACCESS': 0,
'VMEXIT_IO_INSTRUCTION': 0,
'VMEXIT_RDMSR': 0,
'VMEXIT_WRMSR': 0,
'VMEXIT_APICV_ACCESS': 0,
'VMEXIT_APICV_VIRT_EOI': 0,
'VMEXIT_EPT_VIOLATION': 0,
'VMEXIT_EPT_MISCONFIGURATION': 0,
'VMEXIT_RDTSCP': 0,
'VMEXIT_APICV_WRITE': 0,
'VMEXIT_UNHANDLED': 0
}
TIME_IN_EXIT = {
'VMEXIT_EXCEPTION_OR_NMI': 0,
'VMEXIT_EXTERNAL_INTERRUPT': 0,
'VMEXIT_INTERRUPT_WINDOW': 0,
'VMEXIT_CPUID': 0,
'VMEXIT_RDTSC': 0,
'VMEXIT_VMCALL': 0,
'VMEXIT_CR_ACCESS': 0,
'VMEXIT_IO_INSTRUCTION': 0,
'VMEXIT_RDMSR': 0,
'VMEXIT_WRMSR': 0,
'VMEXIT_APICV_ACCESS': 0,
'VMEXIT_APICV_VIRT_EOI': 0,
'VMEXIT_EPT_VIOLATION': 0,
'VMEXIT_EPT_MISCONFIGURATION': 0,
'VMEXIT_RDTSCP': 0,
'VMEXIT_APICV_WRITE': 0,
'VMEXIT_UNHANDLED': 0
}
# 4 * 64bit per trace entry
TRCREC = "QQQQ"
def parse_trace_data(ifile):
"""parse the trace data file
Args:
ifile: input trace data file
Return:
None
"""
global TSC_BEGIN, TSC_END, TOTAL_NR_EXITS
last_ev_id = ''
tsc_enter = 0
tsc_exit = 0
tsc_last_exit_period = 0
fd = open(ifile, 'rb')
while True:
try:
line = fd.read(struct.calcsize(TRCREC))
if not line:
break
(tsc, event, d1, d2) = struct.unpack(TRCREC, line)
event = event & 0xffffffffffff
if event == VM_ENTER:
if TSC_BEGIN == 0:
TSC_BEGIN = tsc
tsc_exit = tsc
TOTAL_NR_EXITS = 0
tsc_enter = tsc
TSC_END = tsc_enter
tsc_last_exit_period = tsc_enter - tsc_exit
if tsc_last_exit_period != 0:
TIME_IN_EXIT[last_ev_id] += tsc_last_exit_period
elif event == VM_EXIT:
tsc_exit = tsc
TSC_END = tsc_exit
TOTAL_NR_EXITS += 1
else:
for key in LIST_EVENTS.keys():
if event == LIST_EVENTS.get(key):
NR_EXITS[key] += 1
last_ev_id = key
else:
# Skip the non-VMEXIT trace event
pass
except (IOError, struct.error) as e:
sys.exit()
def generate_report(ofile, freq):
""" generate analysis report
Args:
ofile: output report
freq: TSC frequency of the device trace data from
Return:
None
"""
global TSC_BEGIN, TSC_END, TOTAL_NR_EXITS
csv_name = ofile + '.csv'
try:
with open(csv_name, 'a') as filep:
f_csv = csv.writer(filep)
total_exit_time = 0
rt_cycle = TSC_END - TSC_BEGIN
assert rt_cycle != 0, "total_run_time in cycle is 0,\
tsc_end %d, tsc_begin %d"\
% (TSC_END, TSC_BEGIN)
rt_sec = float(rt_cycle) / (float(freq) * 1000 * 1000)
for event in LIST_EVENTS:
total_exit_time += TIME_IN_EXIT[event]
print ("Total run time: %d cycles" % (rt_cycle))
print ("TSC Freq: %s MHz" % (freq))
print ("Total run time: %d sec" % (rt_sec))
f_csv.writerow(['Run time(cycles)', 'Run time(Sec)', 'Freq(MHz)'])
f_csv.writerow(['%d' % (rt_cycle),
'%.3f' % (rt_sec),
'%s' % (freq)])
print ("%-28s\t%-12s\t%-12s\t%-24s\t%-16s" % ("Event", "NR_Exit",
"NR_Exit/Sec", "Time Consumed(cycles)", "Time percentage"))
f_csv.writerow(['Exit_Reason',
'NR_Exit',
'NR_Exit/Sec',
'Time Consumed(cycles)',
'Time Percentage'])
for event in LIST_EVENTS:
ev_freq = float(NR_EXITS[event]) / rt_sec
pct = float(TIME_IN_EXIT[event]) * 100 / float(rt_cycle)
print ("%-28s\t%-12d\t%-12.2f\t%-24d\t%-16.2f" %
(event, NR_EXITS[event], ev_freq, TIME_IN_EXIT[event], pct))
row = [event, NR_EXITS[event], '%.2f' % ev_freq, TIME_IN_EXIT[event],
'%2.2f' % (pct)]
f_csv.writerow(row)
ev_freq = float(TOTAL_NR_EXITS) / rt_sec
pct = float(total_exit_time) * 100 / float(rt_cycle)
print("%-28s\t%-12d\t%-12.2f\t%-24d\t%-16.2f"
% ("Total", TOTAL_NR_EXITS, ev_freq, total_exit_time, pct))
row = ["Total", TOTAL_NR_EXITS, '%.2f' % ev_freq, total_exit_time,
'%2.2f' % (pct)]
f_csv.writerow(row)
except IOError as err:
print ("Output File Error: " + str(err))
def analyze_vm_exit(ifile, ofile, freq):
"""do the vm exits analysis
Args:
ifile: input trace data file
ofile: output report file
freq: TSC frequency of the host where we capture the trace data
Return:
None
"""
print("VM exits analysis started... \n\tinput file: %s\n"
"\toutput file: %s.csv" % (ifile, ofile))
parse_trace_data(ifile)
# save report to the output file
generate_report(ofile, freq)