mirror of
https://github.com/projectacrn/acrn-hypervisor.git
synced 2025-09-09 04:39:34 +00:00
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:
50
misc/tools/acrntrace/Makefile
Normal file
50
misc/tools/acrntrace/Makefile
Normal 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
|
171
misc/tools/acrntrace/README.rst
Normal file
171
misc/tools/acrntrace/README.rst
Normal 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.
|
390
misc/tools/acrntrace/acrntrace.c
Normal file
390
misc/tools/acrntrace/acrntrace.c
Normal 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;
|
||||
}
|
86
misc/tools/acrntrace/acrntrace.h
Normal file
86
misc/tools/acrntrace/acrntrace.h
Normal 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;
|
82
misc/tools/acrntrace/sbuf.c
Normal file
82
misc/tools/acrntrace/sbuf.c
Normal 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;
|
||||
}
|
71
misc/tools/acrntrace/sbuf.h
Normal file
71
misc/tools/acrntrace/sbuf.h
Normal 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 */
|
98
misc/tools/acrntrace/scripts/acrnalyze.py
Executable file
98
misc/tools/acrntrace/scripts/acrnalyze.py
Executable 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:])
|
197
misc/tools/acrntrace/scripts/acrntrace_format.py
Executable file
197
misc/tools/acrntrace/scripts/acrntrace_format.py
Executable 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:])
|
22
misc/tools/acrntrace/scripts/formats
Normal file
22
misc/tools/acrntrace/scripts/formats
Normal 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]
|
107
misc/tools/acrntrace/scripts/irq_analyze.py
Executable file
107
misc/tools/acrntrace/scripts/irq_analyze.py
Executable 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)
|
217
misc/tools/acrntrace/scripts/vmexit_analyze.py
Executable file
217
misc/tools/acrntrace/scripts/vmexit_analyze.py
Executable 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)
|
Reference in New Issue
Block a user