mirror of
https://github.com/projectacrn/acrn-hypervisor.git
synced 2025-09-13 12:59:45 +00:00
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:
5
tools/acrn-manager/Makefile
Normal file
5
tools/acrn-manager/Makefile
Normal file
@@ -0,0 +1,5 @@
|
||||
all: acrnctl.c
|
||||
gcc -o acrnctl acrnctl.c -I../../include -Wall -g
|
||||
|
||||
clean:
|
||||
rm -f acrnctl
|
54
tools/acrn-manager/README.rst
Normal file
54
tools/acrn-manager/README.rst
Normal 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
|
729
tools/acrn-manager/acrnctl.c
Normal file
729
tools/acrn-manager/acrnctl.c
Normal 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
5
tools/acrnlog/Makefile
Normal file
@@ -0,0 +1,5 @@
|
||||
all:
|
||||
gcc -g acrnlog.c -o acrnlog -lpthread
|
||||
|
||||
clean:
|
||||
rm acrnlog
|
47
tools/acrnlog/README.rst
Normal file
47
tools/acrnlog/README.rst
Normal 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
525
tools/acrnlog/acrnlog.c
Normal 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;
|
||||
}
|
11
tools/acrnlog/acrnlog.service
Normal file
11
tools/acrnlog/acrnlog.service
Normal 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
5
tools/acrntrace/Makefile
Normal file
@@ -0,0 +1,5 @@
|
||||
all:
|
||||
gcc -o acrntrace acrntrace.c sbuf.c -I. -lpthread
|
||||
|
||||
clean:
|
||||
rm acrntrace
|
49
tools/acrntrace/README.rst
Normal file
49
tools/acrntrace/README.rst
Normal 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
380
tools/acrntrace/acrntrace.c
Normal 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
110
tools/acrntrace/acrntrace.h
Normal 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
78
tools/acrntrace/sbuf.c
Normal 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
94
tools/acrntrace/sbuf.h
Normal 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 */
|
107
tools/acrntrace/scripts/acrnalyze.py
Executable file
107
tools/acrntrace/scripts/acrnalyze.py
Executable 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:])
|
33
tools/acrntrace/scripts/pre_process.sh
Executable file
33
tools/acrntrace/scripts/pre_process.sh
Executable 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
|
274
tools/acrntrace/scripts/vmexit_analyze.py
Executable file
274
tools/acrntrace/scripts/vmexit_analyze.py
Executable 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)
|
198
tools/acrntrace/trace_event.h
Normal file
198
tools/acrntrace/trace_event.h
Normal 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 */
|
Reference in New Issue
Block a user