Move ACRN tools code directory one level higher

The tools directory is moved out of ./devicemodle, to be in parallel with
hypervisor, devicemodel and doc.

Signed-off-by: Yan, Like <like.yan@intel.com>
This commit is contained in:
Yan, Like
2018-05-14 09:06:36 +08:00
committed by Jack Ren
parent b73f1face6
commit 867e56d04b
17 changed files with 0 additions and 0 deletions

View File

@@ -0,0 +1,5 @@
all: acrnctl.c
gcc -o acrnctl acrnctl.c -I../../include -Wall -g
clean:
rm -f acrnctl

View File

@@ -0,0 +1,54 @@
acrnctl
#######
DESCRIPTION
###########
acrnctl: The acrnctl can help user to create, delete, launch and stop UOSs.
It runs under Service OS, and UOSs should be based on acrn-dm
USAGE
#####
To see what it can do, just run:
# acrnctl
or
# acrnctl help
you may see:
support:
list
start
stop
del
add
Use acrnctl [cmd] help for details
There are examples:
(1) add a VM
Each time you can just add one VM. Suppose you have an UOS
launch script, such as launch_UOS.sh
you can run:
# acrnctl add launch_UOS.sh -U 1
vm1-14:59:30 added
Note that, launch script shoud be able to launch ONE UOS. If
it fail, it is better to print some error logs, to tell user
the reason, so that he knows how to solve it.
The vmname is important, the acrnctl searchs VMs by their
names. so duplicated VM names are not allowed. Beside, if the
launch script changes VM name at launch time, acrnctl will
not recgonize it.
(2) delete VMs
# acrnctl del vm1-14:59:30
(3) show VMs
# acrnctl list
vm1-14:59:30 untracked
vm-yocto stop
vm-android stop
(4) start VM
you can start a vm with 'stop' status, each time can start
one VM.
# acrnctl start vm-yocto
(5) stop VM
you can stop VMs, if their status is not 'stop'
# acrnctl stop vm-yocto vm1-14:59:30 vm-android
BUILD
#####
# make

View File

@@ -0,0 +1,729 @@
/*
* ProjectAcrn
* Acrnctl
*
* Copyright (C) 2018 Intel Corporation. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Intel Corporation nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*
* Author: Tao Yuhong <yuhong.tao@intel.com>
*/
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdlib.h>
#include <sys/queue.h>
#include <sys/socket.h>
#include <sys/un.h>
#include "monitor_msg.h"
#define ACRNCTL_OPT_ROOT "/opt/acrn/conf"
/* helper functions */
static int shell_cmd(const char *cmd, char *outbuf, int len)
{
FILE *ptr;
char cmd_buf[256];
int ret;
if (!outbuf)
return system(cmd);
memset(cmd_buf, 0, sizeof(cmd_buf));
memset(outbuf, 0, len);
snprintf(cmd_buf, sizeof(cmd_buf), "%s 2>&1", cmd);
ptr = popen(cmd_buf, "re");
if (!ptr)
return -1;
ret = fread(outbuf, 1, len, ptr);
pclose(ptr);
return ret;
}
static void process_msg(struct vmm_msg *msg)
{
if (msg->len < sizeof(*msg))
return;
switch(msg->msgid) {
case MSG_STR:
printf("%s\n", msg->payload);
break;
default:
printf("Unknown msgid(%d) received\n", msg->msgid);
}
}
/* vm states data and helper functions */
#define ACRN_DM_SOCK_ROOT "/run/acrn"
struct vmm_struct {
char name[128];
unsigned long state;
LIST_ENTRY(vmm_struct) list;
};
enum vm_state {
VM_STATE_UNKNOWN = 0,
VM_CREATED, /* VM created / awaiting start (boot) */
VM_STARTED, /* VM started (booted) */
VM_PAUSED, /* VM paused */
VM_UNTRACKED, /* VM not created by acrnctl, or its launch script can change vm name */
};
static const char *state_str[] = {
[VM_STATE_UNKNOWN] = "unknown",
[VM_CREATED] = "stopped",
[VM_STARTED] = "started",
[VM_PAUSED] = "paused",
[VM_UNTRACKED] = "untracked",
};
static LIST_HEAD(vmm_list_struct, vmm_struct) vmm_head;
static struct vmm_struct *vmm_list_add(char *name)
{
struct vmm_struct *s;
s = calloc(1, sizeof(struct vmm_struct));
if (!s) {
perror("alloc vmm_struct");
return NULL;
}
strcpy(s->name, name);
LIST_INSERT_HEAD(&vmm_head, s, list);
return s;
}
static struct vmm_struct *vmm_find(char *name)
{
struct vmm_struct *s;
LIST_FOREACH(s, &vmm_head, list)
if (!strcmp(name, s->name))
return s;
return NULL;
}
static void vmm_update(void)
{
char cmd[128] = { };
char cmd_out[256] = { };
char *vmname;
char *pvmname = NULL;
struct vmm_struct *s;
snprintf(cmd, sizeof(cmd),
"find %s/add/ -name \"*.sh\" | "
"sed \"s/\\/opt\\/acrn\\/conf\\/add\\///g\" | "
"sed \"s/.sh//g\"", ACRNCTL_OPT_ROOT);
shell_cmd(cmd, cmd_out, sizeof(cmd_out));
vmname = strtok_r(cmd_out, "\n", &pvmname);
while (vmname) {
s = vmm_list_add(vmname);
if (!s)
continue;
s->state = VM_CREATED;
vmname = strtok_r(NULL, "\n", &pvmname);
}
pvmname = NULL;
snprintf(cmd, sizeof(cmd),
"find %s/ -name \"*.socket\" | "
"sed \"s/\\/run\\/acrn\\///g\" | "
"sed \"s/-monitor.socket//g\"", ACRN_DM_SOCK_ROOT);
shell_cmd(cmd, cmd_out, sizeof(cmd_out));
vmname = strtok_r(cmd_out, "\n", &pvmname);
while (vmname) {
s = vmm_find(vmname);
if (s)
s->state = VM_STARTED;
else {
s = vmm_list_add(vmname);
if (s)
s->state = VM_UNTRACKED;
}
vmname = strtok_r(NULL, "\n", &pvmname);
}
}
/* There are acrnctl cmds */
/* command: list */
static void acrnctl_list_help(void)
{
printf("acrnctl list\n"
"\tlist all VMs, shown in %s or %s\n",
ACRNCTL_OPT_ROOT, ACRN_DM_SOCK_ROOT);
}
static int acrnctl_do_list(int argc, char *argv[])
{
struct vmm_struct *s;
int find = 0;
if (argc == 2)
if (!strcmp("help", argv[1])) {
acrnctl_list_help();
return 0;
}
vmm_update();
LIST_FOREACH(s, &vmm_head, list) {
printf("%s\t\t%s\n", s->name, state_str[s->state]);
find++;
}
if (!find)
printf("There are no VMs\n");
return 0;
}
/* command: add */
static void acrnctl_add_help(void)
{
printf("acrnctl add [uos_bash_script]\n"
"\trequires an uos script file\n");
}
static int check_name(const char *name)
{
int i = 0, j = 0;
char illegal[] = "!@#$%^&*, ";
/* Name should start with a letter */
if ((name[0] < 'a' || name[0] > 'z')
&& (name[0] < 'A' || name[0] > 'Z')) {
printf("name not started with latter!\n");
return -1;
}
/* no illegal charactoer */
while (name[i]) {
j = 0;
while (illegal[j]) {
if (name[i] == illegal[j]) {
printf("vmname[%d] is '%c'!\n", i, name[i]);
return -1;
}
j++;
}
i++;
}
if (!strcmp(name, "help"))
return -1;
if (!strcmp(name, "nothing"))
return -1;
return 0;
}
static const char *acrnctl_bin_path;
static int find_acrn_dm;
static int write_tmp_file(int fd, int n, char *word[])
{
int len, ret, i = 0;
char buf[128];
if (!n)
return 0;
len = strlen(word[0]);
if (len >= strlen("acrn-dm")) {
if (!strcmp(word[0] + len - strlen("acrn-dm"), "acrn-dm")) {
find_acrn_dm++;
memset(buf, 0, sizeof(buf));
snprintf(buf, sizeof(buf), "%s gentmpfile",
acrnctl_bin_path);
ret = write(fd, buf, strlen(buf));
if (ret < 0)
return -1;
i++;
}
}
while (i < n) {
memset(buf, 0, sizeof(buf));
snprintf(buf, sizeof(buf), " %s", word[i]);
i++;
ret = write(fd, buf, strlen(buf));
if (ret < 0)
return -1;
}
ret = write(fd, "\n", 1);
if (ret < 0)
return -1;
return 0;
}
#define MAX_FILE_SIZE (4096 * 4)
#define MAX_WORD 64
#define FILE_NAME_LENGTH 128
#define TMP_FILE_SUFFIX ".acrnctl"
static int acrnctl_do_add(int argc, char *argv[])
{
struct vmm_struct *s;
int fd, fd_tmp, ret = 0;
char *buf;
char *word[MAX_WORD], *line;
char *word_p = NULL, *line_p = NULL;
int n_word;
char fname[FILE_NAME_LENGTH + sizeof(TMP_FILE_SUFFIX)];
char cmd[128];
char args[128];
int p, i;
char cmd_out[256];
char vmname[128];
if (argc < 2) {
acrnctl_add_help();
return -1;
}
if (!strcmp("help", argv[1])) {
acrnctl_add_help();
return 0;
}
if (strlen(argv[1]) >= FILE_NAME_LENGTH) {
printf("file name too long: %s\n", argv[1]);
return -1;
}
memset(args, 0, sizeof(args));
p = 0;
for (i = 2; i < argc; i++) {
if (p >= sizeof(args) - 1) {
args[sizeof(args) - 1] = 0;
printf("Too many optional args: %s\n", args);
return -1;
}
p += snprintf(&args[p], sizeof(args) - p, " %s", argv[i]);
}
args[p] = ' ';
fd = open(argv[1], O_RDONLY);
if (fd < 0) {
perror(argv[1]);
ret = -1;
goto open_read_file;
}
buf = calloc(1, MAX_FILE_SIZE);
if (!buf) {
perror("calloc for add vm");
ret = -1;
goto calloc_err;
}
ret = read(fd, buf, MAX_FILE_SIZE);
if (ret >= MAX_FILE_SIZE) {
printf("%s exceed MAX_FILE_SIZE:%d", argv[1], MAX_FILE_SIZE);
ret = -1;
goto file_exceed;
}
/* open tmp file for write */
memset(fname, 0, sizeof(fname));
snprintf(fname, sizeof(fname), "%s%s", argv[1], TMP_FILE_SUFFIX);
fd_tmp = open(fname, O_RDWR | O_CREAT | O_TRUNC, 0666);
if (fd_tmp < 0) {
perror(fname);
ret = -1;
goto open_tmp_file;
}
find_acrn_dm = 0;
line = strtok_r(buf, "\n", &line_p);
while (line) {
word_p = NULL;
n_word = 0;
word[n_word] = strtok_r(line, " ", &word_p);
while (word[n_word]) {
n_word++;
word[n_word] = strtok_r(NULL, " ", &word_p);
}
if (write_tmp_file(fd_tmp, n_word, word)) {
ret = -1;
perror(fname);
goto write_tmpfile;
}
line = strtok_r(NULL, "\n", &line_p);
}
if (!find_acrn_dm) {
printf
("Don't see 'acrn-dm' in %s, maybe it is in another script, "
"this is no supported for now\n", argv[1]);
ret = -1;
goto no_acrn_dm;
}
snprintf(cmd, sizeof(cmd), "mv %s %s.back", argv[1], argv[1]);
system(cmd);
snprintf(cmd, sizeof(cmd), "mv %s %s", fname, argv[1]);
system(cmd);
memset(vmname, 0, sizeof(vmname));
snprintf(cmd, sizeof(cmd), "bash %s%s >./%s.result", argv[1],
args, argv[1]);
ret = shell_cmd(cmd, cmd_out, sizeof(cmd_out));
if (ret < 0)
goto get_vmname;
snprintf(cmd, sizeof(cmd), "grep -a \"acrnctl: \" ./%s.result", argv[1]);
ret = shell_cmd(cmd, cmd_out, sizeof(cmd_out));
if (ret < 0)
goto get_vmname;
ret = sscanf(cmd_out, "acrnctl: %s", vmname);
if (ret != 1) {
ret = -1;
snprintf(cmd, sizeof(cmd), "cat ./%s.result", argv[1]);
shell_cmd(cmd, cmd_out, sizeof(cmd_out));
printf("%s can't reach acrn-dm, "
"please try again when you make sure it can launch an UOS\n"
"result:\n%s\n", argv[1], cmd_out);
goto get_vmname;
}
ret = check_name(vmname);
if (ret) {
printf("\"%s\" is a bad name, please select another name\n",
vmname);
goto get_vmname;
}
snprintf(cmd, sizeof(cmd), "mkdir -p %s/add", ACRNCTL_OPT_ROOT);
system(cmd);
vmm_update();
s = vmm_find(vmname);
if (s) {
printf("%s(%s) already exist, can't add %s%s\n",
vmname, state_str[s->state], argv[1], args);
ret = -1;
goto vm_exist;
}
snprintf(cmd, sizeof(cmd), "cp %s.back %s/add/%s.sh", argv[1],
ACRNCTL_OPT_ROOT, vmname);
system(cmd);
snprintf(cmd, sizeof(cmd), "echo %s >%s/add/%s.args", args,
ACRNCTL_OPT_ROOT, vmname);
system(cmd);
printf("%s added\n", vmname);
vm_exist:
get_vmname:
snprintf(cmd, sizeof(cmd), "rm -f ./%s.result", argv[1]);
system(cmd);
snprintf(cmd, sizeof(cmd), "mv %s %s", argv[1], fname);
system(cmd);
snprintf(cmd, sizeof(cmd), "mv %s.back %s", argv[1], argv[1]);
system(cmd);
no_acrn_dm:
snprintf(cmd, sizeof(cmd), "rm -f %s", fname);
system(cmd);
write_tmpfile:
close(fd_tmp);
open_tmp_file:
file_exceed:
free(buf);
calloc_err:
close(fd);
open_read_file:
return ret;
}
/* command: stop */
static void acrnctl_stop_help(void)
{
printf("acrnctl stop [vmname0] [vmname1] ...\n"
"\t run \"acrnctl list\" to get running VMs\n");
}
static int send_stop_msg(char *vmname)
{
int fd, ret;
struct sockaddr_un addr;
struct vmm_msg msg;
struct timeval timeout;
fd_set rfd, wfd;
char buf[128];
fd = socket(AF_UNIX, SOCK_STREAM, 0);
if (fd < 0) {
printf("%s %d\n", __FUNCTION__, __LINE__);
ret = -1;
goto sock_err;
}
addr.sun_family = AF_UNIX;
snprintf(addr.sun_path, sizeof(addr.sun_path), "%s/%s-monitor.socket",
ACRN_DM_SOCK_ROOT, vmname);
ret = connect(fd, (struct sockaddr *)&addr, sizeof(addr));
if (ret < 0) {
printf("%s %d\n", __FUNCTION__, __LINE__);
goto connect_err;
}
msg.magic = VMM_MSG_MAGIC;
msg.msgid = REQ_STOP;
msg.len = sizeof(msg);
timeout.tv_sec = 1; /* wait 1 second for read/write socket */
timeout.tv_usec = 0;
FD_ZERO(&rfd);
FD_ZERO(&wfd);
FD_SET(fd, &rfd);
FD_SET(fd, &wfd);
select(fd + 1, NULL, &wfd, NULL, &timeout);
if (!FD_ISSET(fd, &wfd)) {
printf("%s %d\n", __FUNCTION__, __LINE__);
goto cant_write;
}
ret = write(fd, &msg, sizeof(msg));
/* wait response */
select(fd + 1, &rfd, NULL, NULL, &timeout);
if (FD_ISSET(fd, &rfd)) {
memset(buf, 0, sizeof(buf));
ret = read(fd, buf, sizeof(buf));
if (ret <= sizeof(buf))
process_msg((void*)&buf);
}
cant_write:
connect_err:
close(fd);
sock_err:
return ret;
}
static int acrnctl_do_stop(int argc, char *argv[])
{
struct vmm_struct *s;
int i;
if (argc < 2) {
acrnctl_stop_help();
return -1;
}
if (!strcmp("help", argv[1])) {
acrnctl_stop_help();
return 0;
}
vmm_update();
for (i = 1; i < argc; i++) {
s = vmm_find(argv[i]);
if (!s) {
printf("can't find %s\n", argv[i]);
continue;
}
if (s->state == VM_CREATED) {
printf("%s is already (%s)\n", argv[i],
state_str[s->state]);
continue;
}
send_stop_msg(argv[i]);
}
return 0;
}
/* command: delete */
static void acrnctl_del_help(void)
{
printf("acrnctl del [vmname0] [vmname1] ...\n"
"\t run \"acrnctl list\" get VM names\n");
}
static int acrnctl_do_del(int argc, char *argv[])
{
struct vmm_struct *s;
int i;
char cmd[128];
if (argc < 2) {
acrnctl_del_help();
return -1;
}
if (!strcmp("help", argv[1])) {
acrnctl_del_help();
return 0;
}
vmm_update();
for (i = 1; i < argc; i++) {
s = vmm_find(argv[i]);
if (!s) {
printf("can't find %s\n", argv[i]);
continue;
}
if (s->state != VM_CREATED) {
printf("can't delete %s(%s)\n", argv[i],
state_str[s->state]);
continue;
}
snprintf(cmd, sizeof(cmd), "rm -f %s/add/%s.sh",
ACRNCTL_OPT_ROOT, argv[i]);
system(cmd);
snprintf(cmd, sizeof(cmd), "rm -f %s/add/%s.args",
ACRNCTL_OPT_ROOT, argv[i]);
system(cmd);
}
return 0;
}
/* command: start */
static void acrnctl_start_help(void)
{
printf("acrnctl start [vmname]\n"
"\t run \"acrnctl list\" get VM names\n"
"\t each time user can only start one VM\n");
}
static int acrnctl_do_start(int argc, char *argv[])
{
struct vmm_struct *s;
char cmd[128];
if (argc != 2) {
acrnctl_start_help();
return -1;
}
if (!strcmp("help", argv[1])) {
acrnctl_start_help();
return 0;
}
vmm_update();
s = vmm_find(argv[1]);
if (!s) {
printf("can't find %s\n", argv[1]);
return -1;
}
if (s->state != VM_CREATED) {
printf("can't start %s(%s)\n", argv[1], state_str[s->state]);
return -1;
}
snprintf(cmd, sizeof(cmd), "bash %s/add/%s.sh $(cat %s/add/%s.args)",
ACRNCTL_OPT_ROOT, argv[1], ACRNCTL_OPT_ROOT, argv[1]);
system(cmd);
return 0;
}
#define ACMD(CMD,FUNC) \
{.cmd = CMD, .func = FUNC,}
struct acrnctl_cmd {
const char *cmd;
int (*func) (int argc, char *argv[]);
} acmds[] = {
ACMD("list", acrnctl_do_list),
ACMD("start", acrnctl_do_start),
ACMD("stop", acrnctl_do_stop),
ACMD("del", acrnctl_do_del),
ACMD("add", acrnctl_do_add),
};
#define NCMD (sizeof(acmds)/sizeof(struct acrnctl_cmd))
static void help_info(void)
{
int i;
printf("support:\n");
for (i = 0; i < NCMD; i++)
printf("\t%s\n", acmds[i].cmd);
printf("Use acrnctl [cmd] help for details\n");
}
int main(int argc, char *argv[])
{
int i;
if (argc == 1) {
help_info();
return 0;
}
acrnctl_bin_path = argv[0];
/* first check acrnctl reserved operations */
if (!strcmp(argv[1], "gentmpfile")) {
printf("\nacrnctl: %s\n", argv[argc - 1]);
return 0;
}
for (i = 0; i < NCMD; i++)
if (!strcmp(argv[1], acmds[i].cmd))
return acmds[i].func(argc - 1, &argv[1]);
/* Reach here means unsupported command */
printf("Unknown command: %s\n", argv[1]);
help_info();
return -1;
}

5
tools/acrnlog/Makefile Normal file
View File

@@ -0,0 +1,5 @@
all:
gcc -g acrnlog.c -o acrnlog -lpthread
clean:
rm acrnlog

47
tools/acrnlog/README.rst Normal file
View File

@@ -0,0 +1,47 @@
ACRNLOG
#######
DESCRIPTION
###########
acrnlog is a userland tool to capture ACRN hypervisor log, it runs as an
SOS service at boot. It captures two kinds of logs:
- log of current running;
- log of last running if crashed and logs remaining.
The path to save log files is /tmp/acrnog/, so the log files would be lost
after reset.
USAGE
#####
The acrnlog tool is launched as a service at boot, with 4 1MB log files limited.
To change the log file limitation:
- temporary change
Stop the acrnlog service:
# systemctl disable acrnlog
Restart acrnlog running at backgroud with size and number of files.
For example:
# acrnlog -n 8 -s 4 &
Use get_loglevel/set_loglevel to query and change the hypervisor loglevel.
The mem_loglevel controls log to be saved using acrnlog, while
console_loglevel controls log to output to console. For example:
ACRN:\>get_loglevel
console_loglevel: 2, mem_loglevel: 4
ACRN:\>set_loglevel 2 5
ACRN:\>get_loglevel
console_loglevel: 2, mem_loglevel: 5
- permanent chagne
Edit /usr/lib/systemd/system/acrnlog.service to attached the -n and -s options to
the ExecStart cmd, and restart the service. For example:
ExecStart=/usr/bin/acrnlog -n 8 -s 4
BUILD&INSTALLATION
##################
# make
copy acrnlog to /usr/bin/ and copy acrnlog.service to /usr/lib/systemd/system/

525
tools/acrnlog/acrnlog.c Normal file
View File

@@ -0,0 +1,525 @@
/*
* Copyright (C) 2018 Intel Corporation. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Intel Corporation nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <linux/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <pthread.h>
#define LOG_ELEMENT_SIZE 80
#define LOG_MSG_SIZE 480
#define PCPU_NUM 4
/* num of physical cpu, not the cpu num seen on SOS */
static unsigned int pcpu_num = PCPU_NUM;
struct hvlog_msg {
__u64 usec; /* timestamp, from tsc reset in usec */
int cpu; /* which physical cpu output the log */
int sev; /* log severity level */
__u64 seq; /* sequence num, used to reorder logs */
size_t len; /* length of message raw string */
char raw[0]; /* raw log string, end with '\0' */
};
/*
* describe the hvlog device, eg., /dev/acrn_hvlog_*
* Note: this is thread-unsafe!
*/
struct hvlog_dev {
int fd;
struct hvlog_msg *msg; /* pointer to msg */
int latched; /* 1 if an sbuf element latched */
char entry_latch[LOG_ELEMENT_SIZE]; /* latch for an sbuf element */
struct hvlog_msg latched_msg; /* latch for parsed msg */
};
static int shell_cmd(const char *cmd, char *outbuf, int len)
{
FILE *ptr;
char cmd_buf[256];
int ret;
if (!outbuf)
return system(cmd);
memset(cmd_buf, 0, sizeof(cmd_buf));
memset(outbuf, 0, len);
snprintf(cmd_buf, sizeof(cmd_buf), "%s 2>&1", cmd);
ptr = popen(cmd_buf, "re");
if (!ptr)
return -1;
ret = fread(outbuf, 1, len, ptr);
pclose(ptr);
return ret;
}
/*
* get pcpu_num, which is equal to num of acrnlog dev
*/
static int get_cpu_num(void)
{
char cmd[128];
char buf[16];
int ret;
snprintf(cmd, sizeof(cmd), "ls /dev/acrn_hvlog_cur_* | wc -l");
ret = shell_cmd(cmd, buf, sizeof(buf));
if (ret <= 0) {
printf("Faile to get cpu number, use default 4\n");
return PCPU_NUM;
}
ret = atoi(buf);
if (ret <= 0) {
printf("Wrong cpu number, use default 4\n");
return PCPU_NUM;
}
return ret;
}
/*
* The function read a complete msg from acrnlog dev.
* read one more sbuf entry if read an entry doesn't end with '\0'
* however, if the new entry contains a new msg - which means the ending char
* is lost, it will be latched to be process next time.
*/
struct hvlog_msg *hvlog_read_dev(struct hvlog_dev *dev)
{
int ret;
size_t len;
struct hvlog_msg *msg[2];
int msg_num;
msg[0] = dev->msg;
msg[1] = &dev->latched_msg;
memset(msg[0], 0, sizeof(struct hvlog_msg) + LOG_MSG_SIZE);
msg_num = 0;
do {
if (dev->latched) {
/* handle the latched msg first */
dev->latched = 0;
memcpy(&msg[0]->raw[msg[0]->len], dev->entry_latch,
LOG_ELEMENT_SIZE);
msg_num++;
memcpy(msg[0], msg[1], sizeof(struct hvlog_msg));
} else {
ret =
read(dev->fd, &msg[0]->raw[msg[0]->len],
LOG_ELEMENT_SIZE);
if (!ret)
break;
/* do we read a new meaasge? */
ret =
sscanf(&msg[0]->raw[msg[0]->len],
"[%lluus][cpu=%d][sev=%d][seq=%llu]:",
&msg[msg_num]->usec, &msg[msg_num]->cpu,
&msg[msg_num]->sev, &msg[msg_num]->seq);
if (ret == 4) {
msg_num++;
/* if we read another new msg, latch it */
/* to process next time */
if (msg_num > 1) {
dev->latched = 1;
memcpy(dev->entry_latch,
&msg[0]->raw[msg[0]->len],
LOG_ELEMENT_SIZE);
break;
}
}
}
if (msg_num == 0) {
/* if head of a message lost, continue to read */
memset(msg[0], 0, sizeof(struct hvlog_msg)
+ LOG_MSG_SIZE);
continue;
}
len = strlen(&msg[0]->raw[msg[0]->len]);
msg[0]->len += len;
} while (len == LOG_ELEMENT_SIZE &&
msg[0]->len < LOG_MSG_SIZE - LOG_ELEMENT_SIZE);
if (!msg[0]->len)
return NULL;
msg[0]->raw[msg[0]->len] = '\n';
msg[0]->raw[msg[0]->len + 1] = 0;
msg[0]->len++;
return msg[0];
}
struct hvlog_dev *hvlog_open_dev(const char *path)
{
struct hvlog_dev *dev;
dev = calloc(1, sizeof(struct hvlog_dev));
if (!dev) {
printf("%s %d\n", __FUNCTION__, __LINE__);
goto open_dev;
}
dev->fd = open(path, O_RDONLY);
if (dev->fd < 0) {
printf("%s %d\n", __FUNCTION__, __LINE__);
goto open_fd;
}
/* actual allocated size is 512B */
dev->msg = calloc(1, sizeof(struct hvlog_msg) + LOG_MSG_SIZE);
if (!dev->msg) {
printf("%s %d\n", __FUNCTION__, __LINE__);
goto alloc_msg;
}
return dev;
open_fd:
close(dev->fd);
alloc_msg:
free(dev);
open_dev:
return NULL;
}
void hvlog_close_dev(struct hvlog_dev *dev)
{
if (!dev)
return;
if (dev->msg)
free(dev->msg);
if (dev->fd > 0)
close(dev->fd);
free(dev);
dev = NULL;
}
/* this is for reading msg from hvlog devices array */
static struct hvlog_data {
struct hvlog_dev *dev;
struct hvlog_msg *msg; /* clean it after use */
} *cur, *last;
/*
* read the earliest msg from each dev, to hvlog_data[].msg
* hvlog_data[] will be used for reordering
*/
static int hvlog_dev_read_msg(struct hvlog_data *data, int num_dev)
{
int i, new_read;
new_read = 0;
for (i = 0; i < num_dev; i++) {
if (data[i].msg)
continue;
if (!data[i].dev)
continue;
data[i].msg = hvlog_read_dev(data[i].dev);
if (data[i].msg)
new_read++;
}
return new_read;
}
static struct hvlog_msg *get_min_seq_msg(struct hvlog_data *data, int num_dev)
{
int i, index_min = -1;
__u64 min_seq = 0;
struct hvlog_msg *msg;
for (i = 0; i < num_dev; i++) {
if (!data[i].msg)
continue;
if (index_min == -1) {
index_min = i;
min_seq = data[i].msg->seq;
continue;
}
if (data[i].msg->seq > min_seq)
continue;
index_min = i;
min_seq = data[i].msg->seq;
}
if (index_min == -1)
return NULL;
msg = data[index_min].msg;
data[index_min].msg = NULL;
return msg;
}
/* this is for log file */
#define LOG_FILE_SIZE (1024*1024)
#define LOG_FILE_NUM 4
static size_t hvlog_log_size = LOG_FILE_SIZE;
static unsigned short hvlog_log_num = LOG_FILE_NUM;
struct hvlog_file {
const char *path;
int fd;
size_t left_space;
unsigned short index;
unsigned short num;
};
static struct hvlog_file cur_log = {
.path = "/tmp/acrnlog/acrnlog_cur",
.fd = -1,
.left_space = 0,
.index = ~0,
.num = LOG_FILE_NUM
};
static struct hvlog_file last_log = {
.path = "/tmp/acrnlog/acrnlog_last",
.fd = -1,
.left_space = 0,
.index = ~0,
.num = LOG_FILE_NUM
};
static int new_log_file(struct hvlog_file *log)
{
char file_name[32] = { };
char cmd[64] = { };
if (log->fd >= 0) {
if (!hvlog_log_size)
return 0;
close(log->fd);
log->fd = -1;
}
snprintf(file_name, sizeof(file_name), "%s.%hu", log->path,
log->index + 1);
snprintf(cmd, sizeof(cmd), "rm -f %s", file_name);
system(cmd);
log->fd = open(file_name, O_RDWR | O_CREAT | O_TRUNC, 0666);
if (log->fd < 0) {
perror(file_name);
return -1;
}
log->left_space = hvlog_log_size;
log->index++;
snprintf(cmd, sizeof(cmd), "rm -f %s.%hu", log->path,
log->index - hvlog_log_num);
system(cmd);
return 0;
}
size_t write_log_file(struct hvlog_file * log, const char *buf, size_t len)
{
int ret;
if (len >= log->left_space)
if (new_log_file(log))
return 0;
ret = write(log->fd, buf, len);
log->left_space -= ret;
return ret;
}
static void *cur_read_func(void *arg)
{
struct hvlog_msg *msg;
while (1) {
hvlog_dev_read_msg(cur, pcpu_num);
msg = get_min_seq_msg(cur, pcpu_num);
if (!msg) {
usleep(500000);
continue;
}
write_log_file(&cur_log, msg->raw, msg->len);
}
return NULL;
}
/* for user optinal args */
static const char optString[] = "s:n:h";
static void display_usage(void)
{
printf("acrnlog - tool to collect ACRN hypervisor log\n"
"[Usage] acrnlog [-s] [size] [-n] [number]\n\n"
"[Options]\n"
"\t-h: print this message\n"
"\t-s: size limitation for each log file, in MB.\n"
"\t 0 means no limitation.\n"
"\t-n: how many files you would like to keep on disk\n"
"[Output] capatured log files under /tmp/acrnlog/\n");
}
static int parse_opt(int argc, char *argv[])
{
int opt, ret;
while ((opt = getopt(argc, argv, optString)) != -1) {
switch (opt) {
case 's':
hvlog_log_size = atoll(optarg) * 1024;
break;
case 'n':
ret = atoi(optarg);
if (ret > 3)
hvlog_log_num = ret;
break;
case 'h':
display_usage();
return -EINVAL;
default:
/* Undefined operation. */
display_usage();
return -EINVAL;
}
}
return 0;
}
static pthread_t cur_thread;
int main(int argc, char *argv[])
{
char name[24];
int i, ret;
int num_cur, num_last;
struct hvlog_msg *msg;
if (parse_opt(argc, argv))
return -1;
system("rm -rf /tmp/acrnlog");
ret = system("mkdir -p /tmp/acrnlog");
if (ret) {
printf("can't create /tmp/acrnlog\n");
return ret;
}
pcpu_num = get_cpu_num();
cur = calloc(pcpu_num, sizeof(struct hvlog_data));
if (!cur) {
printf("Failed to allocate buf for cur log buf\n");
return -1;
}
last = calloc(pcpu_num, sizeof(struct hvlog_data));
if (!last) {
printf("Failed to allocate buf for last log buf\n");
}
num_cur = 0;
for (i = 0; i < pcpu_num; i++) {
snprintf(name, sizeof(name), "/dev/acrn_hvlog_cur_%d", i);
cur[i].dev = hvlog_open_dev(name);
if (!cur[i].dev)
perror(name);
else
num_cur++;
cur[i].msg = NULL;
}
num_last = 0;
for (i = 0; i < pcpu_num; i++) {
snprintf(name, sizeof(name), "/dev/acrn_hvlog_last_%d", i);
last[i].dev = hvlog_open_dev(name);
if (!last[i].dev)
perror(name);
else
num_last++;
last[i].msg = NULL;
}
printf("open cur:%d last:%d\n", num_cur, num_last);
/* create thread to read cur log */
if (num_cur) {
ret = pthread_create(&cur_thread, NULL, cur_read_func, cur);
if (ret) {
printf("%s %d\n", __FUNCTION__, __LINE__);
cur_thread = 0;
}
}
if (num_last && last) {
while (1) {
hvlog_dev_read_msg(last, pcpu_num);
msg = get_min_seq_msg(last, pcpu_num);
if (!msg)
break;
write_log_file(&last_log, msg->raw, msg->len);
}
}
if (cur_thread)
pthread_join(cur_thread, NULL);
for (i = 0; i < pcpu_num; i++) {
hvlog_close_dev(cur[i].dev);
hvlog_close_dev(last[i].dev);
}
free(cur);
if (last)
free(last);
return 0;
}

View File

@@ -0,0 +1,11 @@
[Unit]
Description=ACRN hypervisor log
[Service]
Type=simple
ExecStart=/usr/bin/acrnlog
ExecStop=/usr/bin/killall -s TERM acrnlog
[Install]
WantedBy=multi-user.target

5
tools/acrntrace/Makefile Normal file
View File

@@ -0,0 +1,5 @@
all:
gcc -o acrntrace acrntrace.c sbuf.c -I. -lpthread
clean:
rm acrntrace

View File

@@ -0,0 +1,49 @@
acrntrace
#########
DESCRIPTION
###########
acrntrace: is a tool running on SOS, to capture trace data.
scripts directory includes scripts to analyze the trace data.
USAGE
#####
Capture trace data on SOS
1) Launch acrntrace
Capture buffered trace data:
# acrntrace
or clear buffer before tracing start:
# acrntrace -c
Trace files are created under /tmp/acrntrace/, directory name with time
string eg: 20171115-101605
2) To stop acrntrace
# q <enter>
3) Copy the trace data to linux pc
# scp -r /tmp/acrntrace/20171115-101605/ xxx@10.239.142.239:/home/xxxx/t
race_data
Analyze the trace data on Linux PC
1) Run the python script to analyze the vm_exits:
# acrnalyze.py -i /home/xxxx/trace_data/20171115-101605/0 -o /home/xxxx/trac
e_data/20171115-101605/cpu0 --vm_exit
- "--vm_exit" specify the analysis to do, currently, only vm_exit analysis
is supported.
- A preprocess would be taken out to make the trace data start and end with
an VM_ENTER, and a copy of original data file is saved with suffix ".orig";
- Analysis report would be given on the std output and in a csv file with
name specified via "-o outpu_file";
Script usage:
[Usage] acrnalyze.py [options] [value] ...
[options]
-h: print this message
-i, --ifile=[string]: input file
-o, --ofile=[string]: output file
--vm_exit: to generate vm_exit report
The scripts require bash and python2.
BUILD
#####
# make

380
tools/acrntrace/acrntrace.c Normal file
View File

@@ -0,0 +1,380 @@
/*
* Copyright (C) 2018 Intel Corporation. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Intel Corporation nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <time.h>
#include <signal.h>
#include <errno.h>
#include <pthread.h>
#include <string.h>
#include "acrntrace.h"
#include "trace_event.h"
/* for opt */
static uint64_t period = 10000;
static const char optString[] = "t:hc";
static const char dev_name[] = "/dev/acrn_trace";
static uint32_t flags;
static char trace_file_dir[TRACE_FILE_DIR_LEN];
static reader_struct *reader;
static int pcpu_num = 0;
static void display_usage(void)
{
printf("acrntrace - tool to collect ACRN trace data\n"
"[Usage] acrntrace [-t] [period in msec] [-ch]\n\n"
"[Options]\n"
"\t-h: print this message\n"
"\t-t: period_in_ms: specify polling interval [1-999]\n"
"\t-c: clear the buffered old data\n");
}
static int parse_opt(int argc, char *argv[])
{
int opt, ret;
while ((opt = getopt(argc, argv, optString)) != -1) {
switch (opt) {
case 't':
ret = atoi(optarg);
if (ret <= 0 || ret >=1000) {
pr_err("'-t' require integer between [1-999]\n");
return -EINVAL;
}
period = ret * 1000;
pr_dbg("Period is %lu\n", period);
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 shell_cmd(const char *cmd, char *outbuf, int len)
{
FILE *ptr;
char cmd_buf[256];
int ret;
if (!outbuf)
return system(cmd);
memset(cmd_buf, 0, sizeof(cmd_buf));
memset(outbuf, 0, len);
snprintf(cmd_buf, sizeof(cmd_buf), "%s 2>&1", cmd);
ptr = popen(cmd_buf, "re");
if (!ptr)
return -1;
ret = fread(outbuf, 1, len, ptr);
pclose(ptr);
return ret;
}
static int get_cpu_num(void)
{
char cmd[128];
char buf[16];
int ret;
snprintf(cmd, sizeof(cmd), "ls %s_* | wc -l", dev_name);
ret = shell_cmd(cmd, buf, sizeof(buf));
if (ret <= 0) {
pr_err("Faile to get cpu number, use default 4\n");
return PCPU_NUM;
}
ret = atoi(buf);
if (ret <= 0) {
pr_err("Wrong cpu number, use default 4\n");
return PCPU_NUM;
}
return ret;
}
static double get_cpu_freq(void)
{
char cmd[] =
"cat /proc/cpuinfo | grep -m 1 \"cpu MHz\" | awk '{print $4}'";
char buf[16];
int ret;
double freq = 0;
ret = shell_cmd(cmd, buf, sizeof(buf));
if (ret <= 0) {
pr_err("Faile to get cpu freq, use default 1920MHz\n");
return 1920.00;
}
freq = atof(buf);
if (freq <= 0) {
pr_err("Invalid cpu freq string, use default 1920MHz\n");
return 1920.00;
}
return freq;
}
static int create_trace_file_dir(char *dir)
{
int status;
char cmd[CMD_MAX_LEN];
char time_str[TIME_STR_LEN];
time_t timep;
struct tm *p;
time(&timep);
p = localtime(&timep);
snprintf(time_str, TIME_STR_LEN, "%d%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);
pr_info("start tracing at %s\n", time_str);
snprintf(dir, TRACE_FILE_DIR_LEN, "%s%s", TRACE_FILE_ROOT, time_str);
memset(cmd, 0, CMD_MAX_LEN);
snprintf(cmd, CMD_MAX_LEN, "%s %s", "mkdir -p ", dir);
status = system(cmd);
if (-1 == status)
return -1; /* failed to execute sh */
pr_dbg("dir %s creted\n", dir);
return WIFEXITED(status) ? WEXITSTATUS(status) : EXIT_FAILURE;
}
/* function executed in each consumer thread */
static void reader_fn(param_t * param)
{
int ret;
uint32_t cpuid = param->cpuid;
FILE *fp = param->trace_filep;
shared_buf_t *sbuf = param->sbuf;
trace_ev_t e;
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);
/* write cpu freq to the first line of output file */
fprintf(fp, "CPU Freq: %f\n", get_cpu_freq());
while (1) {
do {
ret = sbuf_get(sbuf, (void *)&e);
if (ret == 0)
break;
else if (ret < 0) {
pr_err("sbuf[%u] read error: %d\n", cpuid, ret);
return;
}
fprintf(fp, "%u | %lu | ", cpuid, e.tsc);
switch (e.id) {
/* defined in trace_event.h */
/* for each ev type */
ALL_CASES;
}
} while (ret > 0);
usleep(period);
}
}
static int create_reader(reader_struct * reader, uint32_t cpu)
{
char trace_file_name[TRACE_FILE_NAME_LEN];
snprintf(reader->dev_name, DEV_PATH_LEN, "%s_%u", dev_name, cpu);
reader->param.cpuid = cpu;
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 cpu%d, errno %d\n", cpu, errno);
reader->param.sbuf = NULL;
return -2;
}
pr_dbg("sbuf[%d]:\nmagic_num: %lx\nele_num: %u\n ele_size: %u\n",
cpu, reader->param.sbuf->magic, reader->param.sbuf->ele_num,
reader->param.sbuf->ele_size);
snprintf(trace_file_name, TRACE_FILE_NAME_LEN, "%s/%d", trace_file_dir,
cpu);
reader->param.trace_filep = fopen(trace_file_name, "w+");
if (!reader->param.trace_filep) {
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", cpu);
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_filep) {
fflush(reader->param.trace_filep);
fclose(reader->param.trace_filep);
reader->param.trace_filep = NULL;
}
}
static void handle_on_exit(void)
{
uint32_t cpu;
/* if nothing to release */
if (!(flags & FLAG_TO_REL))
return;
pr_info("exiting - to release resources...\n");
foreach_cpu(cpu)
destory_reader(&reader[cpu]);
}
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 cpu = 0;
/* parse options */
if (parse_opt(argc, argv))
exit(EXIT_FAILURE);
/* how many cpus */
pcpu_num = get_cpu_num();
reader = calloc(1, sizeof(reader_struct) * pcpu_num);
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);
}
atexit(handle_on_exit);
/* acquair res for each trace dev */
flags |= FLAG_TO_REL;
foreach_cpu(cpu)
if (create_reader(&reader[cpu], cpu) < 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 (getchar() != 'q')
printf("q <enter> to quit:\n");
out_free:
foreach_cpu(cpu)
destory_reader(&reader[cpu]);
free(reader);
flags &= ~FLAG_TO_REL;
return EXIT_SUCCESS;
}

110
tools/acrntrace/acrntrace.h Normal file
View File

@@ -0,0 +1,110 @@
/*
* Copyright (C) 2018 Intel Corporation. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Intel Corporation nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#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 36
#define TRACE_FILE_DIR_LEN (TRACE_FILE_NAME_LEN - 2)
#define TRACE_FILE_ROOT "/tmp/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_cpu(cpu) \
for ((cpu) = 0; (cpu) < (pcpu_num); (cpu)++)
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 cpuid;
int exit_flag;
FILE *trace_filep;
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;

78
tools/acrntrace/sbuf.c Normal file
View File

@@ -0,0 +1,78 @@
/*
* Copyright (C) 2018 Intel Corporation. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Intel Corporation nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <asm/errno.h>
#include <string.h>
#include <stdbool.h>
#include "sbuf.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_clear_buffered(shared_buf_t *sbuf)
{
if (sbuf == NULL)
return -EINVAL;
sbuf->head = sbuf->tail;
return 0;
}

94
tools/acrntrace/sbuf.h Normal file
View File

@@ -0,0 +1,94 @@
/*
* Copyright (C) 2018 Intel Corporation. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Intel Corporation nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#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_clear_buffered(shared_buf_t *sbuf);
#endif /* SHARED_BUF_H */

View File

@@ -0,0 +1,107 @@
#!/usr/bin/python2
# -*- coding: UTF-8 -*-
"""
This is the main script of arnalyzer, which:
- parse the options
- pre-process the trace data file
- call a specific script to do analysis
"""
import sys
import getopt
import os
from vmexit_analyze import analyze_vm_exit
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
--vm_exit: to generate vm_exit report
'''
def do_analysis(ifile, ofile, analyzer):
"""do the specific analysis
Args:
ifile: input trace data file
ofile: output analysis report file
analyzer: a function do the specific analysis
Returns:
None
Raises:
NA
"""
analyzer(ifile, ofile)
# pre process to make sure the trace data start and end with VMENTER
def pre_process(ifile, evstr):
"""invoke sh script to preprocess the data file
Args:
ifile: input trace data file
evstr: event string, after the processing, the data file should
start and end with the string
Returns:
status of sh script execution
Raises:
NA
"""
status = os.system('bash pre_process.sh %s %s' % (ifile, evstr))
return status
def main(argv):
"""Main enterance function
Args:
argv: arguments string
Returns:
None
Raises:
GetoptError
"""
inputfile = ''
outputfile = ''
opts_short = "hi:o:"
opts_long = ["ifile=", "ofile=", "vm_exit"]
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 == "--vm_exit":
analyzer = analyze_vm_exit
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'
status = pre_process(inputfile, 'VM_ENTER')
if status == 0:
do_analysis(inputfile, outputfile, analyzer)
else:
print "Invalid trace data file %s" % (inputfile)
if __name__ == "__main__":
main(sys.argv[1:])

View File

@@ -0,0 +1,33 @@
#!/bin/bash
IFILE=$1
STR=$2
BAK_FILE=${IFILE}.orig
if [ ! -f $BAK_FILE ]; then
cp $IFILE $BAK_FILE
fi
echo $IFILE, $BAK_FILE, $STR
FIRST_LINE=$(grep -n $STR -m 1 $IFILE | awk -F: '{print $1}')
LAST_LINE=$(grep -n $STR $IFILE | tail -1 | awk -F: '{print $1}')
TOTAL_LINE=$(wc -l $IFILE | awk '{print $1}')
echo $FIRST_LINE, $LAST_LINE, $TOTAL_LINE
if [[ $FIRST_LINE -ge $LAST_LINE || $LAST_LINE -gt $TOTAL_LINE ]]; then
exit 1
fi
if [[ $LAST_LINE -lt $TOTAL_LINE ]]; then
let LAST_LINE=$((LAST_LINE))+1
echo "sed -i ${LAST_LINE},${TOTAL_LINE}d $IFILE"
sed -i ${LAST_LINE}','${TOTAL_LINE}'d' $IFILE
fi
if [[ $FIRST_LINE -gt 2 ]]; then
let FIRST_LINE=$((FIRST_LINE))-1
echo "sed -i 2,${FIRST_LINE}d $IFILE"
sed -i '2,'${FIRST_LINE}'d' $IFILE
fi

View File

@@ -0,0 +1,274 @@
#!/usr/bin/python2
# -*- coding: UTF-8 -*-
"""
This script defines the function to do the vm_exit analysis
"""
import csv
TSC_BEGIN = 0L
TSC_END = 0L
TOTAL_NR_EXITS = 0L
LIST_EVENTS = [
'VMEXIT_EXCEPTION_OR_NMI',
'VMEXIT_EXTERNAL_INTERRUPT',
'VMEXIT_INTERRUPT_WINDOW',
'VMEXIT_CPUID',
'VMEXIT_RDTSC',
'VMEXIT_VMCALL',
'VMEXIT_CR_ACCESS',
'VMEXIT_IO_INSTRUCTION',
'VMEXIT_RDMSR',
'VMEXIT_WRMSR',
'VMEXIT_APICV_ACCESS',
'VMEXIT_APICV_VIRT_EOI',
'VMEXIT_EPT_VIOLATION',
'VMEXIT_EPT_VIOLATION_GVT',
'VMEXIT_EPT_MISCONFIGURATION',
'VMEXIT_RDTSCP',
'VMEXIT_APICV_WRITE',
'VMEXIT_UNHANDLED'
]
NR_EXITS = {
'VM_EXIT': 0,
'VM_ENTER': 0,
'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_VIOLATION_GVT': 0,
'VMEXIT_EPT_MISCONFIGURATION': 0,
'VMEXIT_RDTSCP': 0,
'VMEXIT_APICV_WRITE': 0,
'VMEXIT_UNHANDLED': 0
}
TIME_IN_EXIT = {
'VM_EXIT': 0,
'VM_ENTER': 0,
'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_VIOLATION_GVT': 0,
'VMEXIT_EPT_MISCONFIGURATION': 0,
'VMEXIT_RDTSCP': 0,
'VMEXIT_APICV_WRITE': 0,
'VMEXIT_UNHANDLED': 0
}
IRQ_EXITS = {}
def count_irq(info):
vec = info[5:15]
if IRQ_EXITS.has_key(vec):
IRQ_EXITS[vec] += 1
else:
IRQ_EXITS[vec] = 1
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
tsc_enter = 0L
tsc_exit = 0L
tsc_last_exit_period = 0L
ev_id = ''
last_ev_id = ''
try:
ifp = open(ifile)
line = ifp.readline()
# should preprocess to make sure first event is VM_ENTER
while 1:
line = ifp.readline()
if line == '':
ifp.close()
break
try:
(cpuid, tsc, payload) = line.split(" | ")
(ev_id, info) = payload.split(":")
except ValueError, execp:
print execp
print line
continue
if ev_id == 'VM_ENTER':
if TSC_BEGIN == 0:
TSC_BEGIN = long(tsc)
tsc_exit = long(tsc)
TOTAL_NR_EXITS = 0
tsc_enter = long(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 ev_id == 'VM_EXIT':
tsc_exit = long(tsc)
TSC_END = tsc_exit
TOTAL_NR_EXITS += 1
elif ev_id.startswith('VMEXIT_'):
if (ev_id == 'VMEXIT_EPT_VIOLATION'
and (eval(info[6:24]) & 0x38) == 0x28):
ev_id = 'VMEXIT_EPT_VIOLATION_GVT'
if ev_id.startswith('VMEXIT_EX'):
count_irq(info)
NR_EXITS[ev_id] += 1
last_ev_id = ev_id
else:
# skip the non-VMEXIT trace event
pass
except IOError as err:
print "Input File Error: " + str(err)
finally:
if 'ifp' in locals():
ifp.close()
def generate_report(ofile, freq):
""" generate analysis report
Args:
ofile: output report
freq: CPU 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, 'w') as filep:
f_csv = csv.writer(filep)
total_exit_time = 0L
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 "CPU Freq: %f 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),
'%d' % (freq)])
print "Event \tNR_Exit \tNR_Exit/Sec \tTime Consumed \tTime 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 ("%s \t%d \t%.2f \t%d \t%2.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("Total \t%d \t%.2f \t%d \t%2.2f"
% (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)
# insert a empty row to separate two tables
f_csv.writerow([''])
print "\nVector \t\tCount \tNR_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 "%s \t %d \t%.2f" % (e,IRQ_EXITS[e], pct)
f_csv.writerow([e, IRQ_EXITS[e], '%.2f' % pct])
except IOError as err:
print "Output File Error: " + str(err)
def get_freq(ifile):
""" get cpu freq from the first line of trace file
Args:
ifile: input trace data file
Return:
cpu frequency
"""
try:
ifp = open(ifile)
line = ifp.readline()
freq = float(line[10:])
except IOError as err:
print "Failed to get cpu freq"
freq = 1920.00
finally:
if 'ifp' in locals():
ifp.close()
return freq
def analyze_vm_exit(ifile, ofile):
"""do the vm exits analysis
Args:
ifile: input trace data file
ofile: output report file
Return:
None
"""
print("VM exits analysis started... \n\tinput file: %s\n"
"\toutput file: %s.csv" % (ifile, ofile))
freq = get_freq(ifile)
parse_trace_data(ifile)
# save report to the output file
generate_report(ofile, freq)

View File

@@ -0,0 +1,198 @@
/*
* Copyright (C) 2018 Intel Corporation. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Intel Corporation nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef TRACE_EVENT_H
#define TRACE_EVENT_H
#define GEN_CASE(id) case (id):{ id##_FMT; break; }
/* TIMER */
#define TRACE_TIMER_ACTION_ADDED 0x1
#define TRACE_TIMER_ACTION_PCKUP 0x2
#define TRACE_TIMER_ACTION_UPDAT 0x3
#define TRACE_TIMER_IRQ 0x4
#define TRACE_VM_EXIT 0x10
#define TRACE_VM_ENTER 0X11
#define TRC_VMEXIT_ENTRY 0x10000
#define TRC_VMEXIT_EXCEPTION_OR_NMI (TRC_VMEXIT_ENTRY + 0x00000000)
#define TRC_VMEXIT_EXTERNAL_INTERRUPT (TRC_VMEXIT_ENTRY + 0x00000001)
#define TRC_VMEXIT_INTERRUPT_WINDOW (TRC_VMEXIT_ENTRY + 0x00000002)
#define TRC_VMEXIT_CPUID (TRC_VMEXIT_ENTRY + 0x00000004)
#define TRC_VMEXIT_RDTSC (TRC_VMEXIT_ENTRY + 0x00000010)
#define TRC_VMEXIT_VMCALL (TRC_VMEXIT_ENTRY + 0x00000012)
#define TRC_VMEXIT_CR_ACCESS (TRC_VMEXIT_ENTRY + 0x0000001C)
#define TRC_VMEXIT_IO_INSTRUCTION (TRC_VMEXIT_ENTRY + 0x0000001E)
#define TRC_VMEXIT_RDMSR (TRC_VMEXIT_ENTRY + 0x0000001F)
#define TRC_VMEXIT_WRMSR (TRC_VMEXIT_ENTRY + 0x00000020)
#define TRC_VMEXIT_EPT_VIOLATION (TRC_VMEXIT_ENTRY + 0x00000030)
#define TRC_VMEXIT_EPT_MISCONFIGURATION (TRC_VMEXIT_ENTRY + 0x00000031)
#define TRC_VMEXIT_RDTSCP (TRC_VMEXIT_ENTRY + 0x00000033)
#define TRC_VMEXIT_APICV_WRITE (TRC_VMEXIT_ENTRY + 0x00000038)
#define TRC_VMEXIT_APICV_ACCESS (TRC_VMEXIT_ENTRY + 0x00000039)
#define TRC_VMEXIT_APICV_VIRT_EOI (TRC_VMEXIT_ENTRY + 0x0000003A)
#define TRC_VMEXIT_UNHANDLED 0x20000
#define TRACE_CUSTOM 0xFC
#define TRACE_FUNC_ENTER 0xFD
#define TRACE_FUNC_EXIT 0xFE
#define TRACE_STR 0xFF
/* TRACE_EVENTID_MAX 256 */
#define PR(fmt, ...) fprintf((fp), fmt, ##__VA_ARGS__);
#define TRACE_TIMER_ACTION_ADDED_FMT \
{PR("TIMER_ACTION ADDED: ID %d, deadline %lx total %d\n", \
(e).a, ((uint64_t)((e).c)<<32)|(e).b, (e).d); }
#define TRACE_TIMER_ACTION_PCKUP_FMT \
{PR("TIMER_ACTION PCKUP: ID %d, deadline %lx total %d\n", \
(e).a, ((uint64_t)((e).c)<<32)|(e).b, (e).d); }
#define TRACE_TIMER_ACTION_UPDAT_FMT \
{PR("TIMER_ACTION UPDAT: ID %d, deadline %lx total %d\n", \
(e).a, ((unsigned long)((e).c)<<32)|(e).b, (e).d); }
#define TRACE_TIMER_IRQ_FMT \
PR("TIMER_IRQ total: %lx\n", (e).e)
#define TRACE_CUSTOM_FMT \
PR("CUSTOM: 0x%lx 0x%lx\n", (e).e, (e).f)
#define TRACE_FUNC_ENTER_FMT \
PR("ENTER: %s\n", (e).str)
#define TRACE_FUNC_EXIT_FMT \
PR("EXIT : %s\n", (e).str)
#define TRACE_STR_FMT \
PR("STR: %s\n", (e).str)
#define TRACE_VM_EXIT_FMT \
PR("VM_EXIT: exit_reason 0x%016lx, guest_rip 0x%016lx\n", \
(e).e, (e).f)
#define TRACE_VM_ENTER_FMT \
PR("VM_ENTER:\n")
#define TRC_VMEXIT_EXCEPTION_OR_NMI_FMT \
PR("VMEXIT_EXCEPTION_OR_NMI: \
vec 0x%08x, err_code 0x%08x, type %d\n", \
(e).a, (e).b, (e).c)
#define TRC_VMEXIT_EXTERNAL_INTERRUPT_FMT \
PR("VMEXIT_EXTERNAL_INTERRUPT: vec 0x%08lx\n", (e).e)
#define TRC_VMEXIT_INTERRUPT_WINDOW_FMT \
PR("VMEXIT_INTERRUPT_WINDOW:\n")
#define TRC_VMEXIT_CPUID_FMT \
PR("VMEXIT_CPUID: vcpuid %lu\n", (e).e)
#define TRC_VMEXIT_RDTSC_FMT \
PR("VMEXIT_RDTSC: host_tsc 0x%016lx, tsc_offset 0x%016lx\n", \
(e).e, (e).f)
#define TRC_VMEXIT_VMCALL_FMT \
PR("VMEXIT_VMCALL: vmid %lu, hypercall_id %lu\n", \
(e).e, (e).f)
#define TRC_VMEXIT_CR_ACCESS_FMT \
PR("VMEXIT_CR_ACCESS: op %s, rn_nr %lu\n", \
(e).e?"Read":"Write", (e).f)
#define TRC_VMEXIT_IO_INSTRUCTION_FMT \
PR("VMEXIT_IO_INSTRUCTION: \
port %u, dir %u, sz %u, cur_ctx_idx %u\n", \
(e).a, (e).b, (e).c, (e).d)
#define TRC_VMEXIT_RDMSR_FMT \
PR("VMEXIT_RDMSR: msr 0x%08lx, val 0x%08lx\n", \
(e).e, (e).f)
#define TRC_VMEXIT_WRMSR_FMT \
PR("VMEXIT_WRMSR: msr 0x%08lx, val 0x%08lx\n", \
(e).e, (e).f)
#define TRC_VMEXIT_EPT_VIOLATION_FMT \
PR("VMEXIT_EPT_VIOLATION: qual 0x%016lx, gpa 0x%016lx\n", \
(e).e, (e).f)
#define TRC_VMEXIT_EPT_MISCONFIGURATION_FMT \
PR("VMEXIT_EPT_MISCONFIGURATION:\n")
#define TRC_VMEXIT_RDTSCP_FMT \
PR("VMEXIT_RDTSCP: guest_tsc 0x%lx, tsc_aux 0x%lx\n", \
(e).e, (e).f)
#define TRC_VMEXIT_APICV_WRITE_FMT \
PR("VMEXIT_APICV_WRITE: offset 0x%lx\n", (e).e)
#define TRC_VMEXIT_APICV_ACCESS_FMT \
PR("VMEXIT_APICV_ACCESS:\n")
#define TRC_VMEXIT_APICV_VIRT_EOI_FMT \
PR("VMEXIT_APICV_VIRT_EOI: vec 0x%08lx\n", (e).e)
#define TRC_VMEXIT_UNHANDLED_FMT \
PR("VMEXIT_UNHANDLED: 0x%08lx\n", (e).e)
#define ALL_CASES \
GEN_CASE(TRACE_TIMER_ACTION_ADDED); \
GEN_CASE(TRACE_TIMER_ACTION_PCKUP); \
GEN_CASE(TRACE_TIMER_ACTION_UPDAT); \
GEN_CASE(TRACE_TIMER_IRQ); \
GEN_CASE(TRACE_CUSTOM); \
GEN_CASE(TRACE_STR); \
GEN_CASE(TRACE_FUNC_ENTER); \
GEN_CASE(TRACE_FUNC_EXIT); \
GEN_CASE(TRACE_VM_EXIT); \
GEN_CASE(TRACE_VM_ENTER); \
GEN_CASE(TRC_VMEXIT_EXCEPTION_OR_NMI); \
GEN_CASE(TRC_VMEXIT_EXTERNAL_INTERRUPT);\
GEN_CASE(TRC_VMEXIT_INTERRUPT_WINDOW); \
GEN_CASE(TRC_VMEXIT_CPUID); \
GEN_CASE(TRC_VMEXIT_RDTSC); \
GEN_CASE(TRC_VMEXIT_VMCALL); \
GEN_CASE(TRC_VMEXIT_CR_ACCESS); \
GEN_CASE(TRC_VMEXIT_IO_INSTRUCTION); \
GEN_CASE(TRC_VMEXIT_RDMSR); \
GEN_CASE(TRC_VMEXIT_WRMSR); \
GEN_CASE(TRC_VMEXIT_EPT_VIOLATION); \
GEN_CASE(TRC_VMEXIT_EPT_MISCONFIGURATION);\
GEN_CASE(TRC_VMEXIT_RDTSCP); \
GEN_CASE(TRC_VMEXIT_APICV_WRITE); \
GEN_CASE(TRC_VMEXIT_APICV_ACCESS); \
GEN_CASE(TRC_VMEXIT_APICV_VIRT_EOI); \
GEN_CASE(TRC_VMEXIT_UNHANDLED); \
#endif /* TRACE_EVENT_H */