sample app: initial import of the sample app

This sample application allows the user to get the hypervisor and a simple Virtual Machine Scenario to run
This sample application assumes the user is following the sample app guide in the acrn hypervisor documentation

Tracked-On: #7820
Signed-off-by: Matthew Leon <matthew.leon@intel.com>
This commit is contained in:
Matthew Leon 2022-06-27 10:47:39 -07:00 committed by acrnsi-robot
parent c6fcda2a0d
commit d8f55c7bca
11 changed files with 940 additions and 0 deletions

View File

@ -0,0 +1,7 @@
This directory contains the software that is necessary to run a real-time application between a real-time VM and a standard VM using the acrn-hypervisor.
The rtvm directory contains the code required to pipe data from cyclictest to the uservm using the inter-vm shared memory feature that acrn-hypervisor exposes to its VMs.
The uservm directory contains the code required to read the piped data from the rtvm, process the data, and display that data over a web application that can be accessed from the hypervisor's service VM.
To build and run the applications, copy this repo to your VMs, run make in the directory that corresponds to the VM that you are running, and then follow the sample app guide in the acrn-hypervisor documentation.

View File

@ -0,0 +1,12 @@
CC = gcc
CP = g++
CFLAGS = -Wall -Wextra -Wabi=11 -pedantic
LDLIBS = -lrt -pthread
all: rtApp.c
$(CC) $(CFLAGS) -o rtapp rtApp.c ivshmemlib.c
clean:
rm rtapp

View File

@ -0,0 +1,148 @@
/*
* Copyright (C) 2022 Intel Corporation.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include "ivshmemlib.h"
//The shared memory region that is used for inter-vm shared memory
char *ivshmem_ptr = NULL;
/*
int setup_ivshmem_region(const char *f_path)
input: char *f_path - A string containing the pci resource2 filepath
output: int - Whether the setup succeeded or not
This function attempts to open the file whose path is f_path
On success it returns 0
On failure it returns -1
*/
int setup_ivshmem_region(const char *f_path)
{
//Open the file so we can map it into memory
int pci_file = open(f_path, O_RDWR | O_SYNC);
if (pci_file == failure) {
perror("Failed to open the resource2 file\n");
return failure;
}
//Map the file into memory
ivshmem_ptr = (char *)mmap(0, REGION_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, pci_file , 0);
close(pci_file);
if (!ivshmem_ptr) {
perror("Failed to map the shared memory region into our address space\n");
return failure;
}
return success;
}
/*
int close_ivshmem_region(void)
output: int - Whether unmapping the region succeeded or not.
This function closes the ivshmem region
On success it returns 0
On failure it returns -1
*/
int close_ivshmem_region(void)
{
int ret_val = failure;
//Determine if ivshmem region is set up
if (ivshmem_ptr) {
ret_val = munmap(ivshmem_ptr, REGION_SIZE);
ivshmem_ptr = NULL;
}
else
printf("Ivshmem region is not set up.");
return ret_val;
}
/*
size_t read_ivshmem_region(char *ivshmemPtr, char *user_ptr, size_t size)
input: char *user_ptr - The memory to write to
input: size_t size - the number of bytes to read from the memory
output: size_t - The number of bytes that were successfully read or -1 on failure
This function reads from the ivshmem region and copies size - 1 bytes
from the shared memory region to the user_ptr
and null-terminates the string
*/
size_t read_ivshmem_region(char *user_ptr, size_t size)
{
//Number of bytes copied
size_t ret = failure;
//Make sure that we actually need to read something
if (size == 0)
return ret;
//Determine if ivshmem region is set up
if (ivshmem_ptr) {
//Do the copy and zero out the ivshmem region
strncpy(user_ptr, ivshmem_ptr, size - 1);
user_ptr[size] = 0;
ret = strlen(user_ptr);
bzero(ivshmem_ptr, ret);
}
else
printf("Ivshmem region is not set up.");
return ret;
}
/*
size_t write_ivshmem_region(char *ivshmemPtr, char *user_ptr, size_t size)
input: char *user_ptr - The memory to read from
input: int size - the number of bytes to write from the memory
output: int - The number of bytes that were successfully written or -1 on failure
This function reads from the user_ptr and copies size - 1 bytes
to the shared memory region
and null-terminates the string
*/
size_t write_ivshmem_region(char *user_ptr, size_t size)
{
//Return value that holds the amount of bytes that were copied from the user_ptr
size_t ret = failure;
//Make sure that we need to actually write something
if (size == 0)
return ret;
//Determine if ivshmem region is set up
if (ivshmem_ptr) {
//Do the copy and zero out the user_ptr
strncpy(ivshmem_ptr, user_ptr, size - 1);
user_ptr[size] = 0;
ret = strlen(ivshmem_ptr);
bzero(user_ptr, ret);
}
else
printf("Ivshmem region is not set up.");
return ret;
}

View File

@ -0,0 +1,23 @@
/*
* Copyright (C) 2022 Intel Corporation.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/mman.h>
//The size of the shared memory region
#define REGION_SIZE 32768
enum return_vals {success = 0, failure = -1};
int setup_ivshmem_region(const char*);
int close_ivshmem_region(void);
size_t read_ivshmem_region(char *, size_t);
size_t write_ivshmem_region(char *, size_t);

View File

@ -0,0 +1,104 @@
/*
* Copyright (C) 2022 Intel Corporation.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <stdio.h>
#include <fcntl.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/stat.h>
#include "ivshmemlib.h"
#define BUFFERSIZE 256
//Used for reading output from cyclictest
int data_pipe = -1;
//Used for handling signals
void sig_handler(int);
int main(void)
{
//First make sure the user is root
if (geteuid() != 0) {
printf("You need to run this program as root!!!\n");
return failure;
}
//Used for holding the data from cyclictest
char data_buffer[BUFFERSIZE] = {0};
//Used for sanitizing the data
char *start_stat = NULL;
//Set up signal handler for when we get interrupt
signal(SIGINT, sig_handler);
//Used for reading output from cyclictest
data_pipe = open("data_pipe", O_RDWR);
if (data_pipe == failure) {
perror("Failed to open a fifo pipe");
return failure;
}
//Open the shared memory region
if (setup_ivshmem_region("/sys/class/uio/uio0/device/resource2") == failure) {
perror("Failed to open the shared memory region");
close(data_pipe);
return failure;
}
//Loop forever, reading and writing the data from cyclictest to the uservm
while (1) {
//Read the data
bzero(data_buffer, BUFFERSIZE);
read(data_pipe, data_buffer, BUFFERSIZE);
//Get the sample stat
start_stat = strstr(data_buffer, "T:");
if (start_stat != NULL)
//Send the data
write_ivshmem_region(start_stat, BUFFERSIZE);
}
//Close the shared memory region and the data pipe now that we don't need them
close_ivshmem_region();
close(data_pipe);
return success;
}
/*
void sig_handler(int signum)
input: int - the signal value
This function will get run when a signal is sent to the process and will gracefully
shut down the program
*/
void sig_handler(int signum)
{
fprintf(stderr, "Received signal: %d\n", signum);
//Close the shared memory region and the data pipe now that we don't need them
close_ivshmem_region();
if (data_pipe != -1)
close(data_pipe);
exit(-1);
}

View File

@ -0,0 +1,12 @@
CC = gcc
CP = g++
CFLAGS = -Wall -Wextra -Wabi=11 -pedantic
LDLIBS = -lrt -pthread
all: userApp.cpp
$(CP) $(CFLAGS) -o userapp userApp.cpp ivshmemlib.c $(LDLIBS)
clean:
rm userapp

View File

@ -0,0 +1,93 @@
"""
* Copyright (C) 2022 Intel Corporation.
*
* SPDX-License-Identifier: BSD-3-Clause
"""
from flask import Flask
from flask import send_file
import numpy as np
import pandas
import random
import matplotlib.pyplot as plt
import posix_ipc as ipc
import mmap
app = Flask(__name__)
#Runs when the user goes to our ip address
@app.route('/')
def histapp():
#Create and save the figure
create_hist()
#Send the histogram as a webpage to the user
return send_file("hist.png", mimetype='image/png')
#Creates the user histogram and saves to hist.png
def create_hist():
#Go to the beginning of the shared memory region
shm.seek(0)
#Get the data
web_sem.acquire()
data = shm.readline();
web_sem.release()
#Transform the data into an array that matplotlib can understand
dataset = transform_data(data)
#Clear the figure and recreate from the new data
plt.clf()
#Setup the figure and save it
figure = plt.hist(dataset)
#plt.title("Latencies where count = " + str(total_count))
plt.title("Latencies percentages")
plt.xlabel("Latency Value (ms)")
plt.ylabel("Frequency")
plt.savefig("hist.png")
return figure
def transform_data(data_string):
transformed_data_values = []
str_data = data_string.decode("utf-8")
str_data = str_data.replace('\n',"")
data_values = str_data.split()
#Holds the count of latencies that we have
total_count = data_values[0]
#Used for transforming the data values
data_percentages = data_values[1:]
if (len(data_percentages) % 2 != 0):
return transformed_data_values
#Transform the data into a list that can be fed to matplotlib
for i in range(0, int(len(data_percentages) / 2)):
transformed_data_values += ([data_values[i*2]] * data_values[i*2 + 1])
return transformed_data_values
if __name__ == '__main__':
#Set up shared memory between userapp and the webserver
shm_path = "/pyservershm"
shm_size = 1048576
shm_f = ipc.SharedMemory(shm_path, 0, size=shm_size)
shm = mmap.mmap(shm_f.fd, shm_size)
#Set up the semaphore to maintaine synchronization
web_sem = ipc.Semaphore("/pyserversem", 0, 0o0774)
#Run the webserver
app.run(host="0.0.0.0", port=80, debug=True)

View File

@ -0,0 +1,148 @@
/*
* Copyright (C) 2022 Intel Corporation.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include "ivshmemlib.h"
//The shared memory region that is used for inter-vm shared memory
char *ivshmem_ptr = NULL;
/*
int setup_ivshmem_region(const char *f_path)
input: char *f_path - A string containing the pci resource2 filepath
output: int - Whether the setup succeeded or not
This function attempts to open the file whose path is f_path
On success it returns 0
On failure it returns -1
*/
int setup_ivshmem_region(const char *f_path)
{
//Open the file so we can map it into memory
int pci_file = open(f_path, O_RDWR | O_SYNC);
if (pci_file == failure) {
perror("Failed to open the resource2 file\n");
return failure;
}
//Map the file into memory
ivshmem_ptr = (char *)mmap(0, REGION_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, pci_file , 0);
close(pci_file);
if (!ivshmem_ptr) {
perror("Failed to map the shared memory region into our address space\n");
return failure;
}
return success;
}
/*
int close_ivshmem_region(void)
output: int - Whether unmapping the region succeeded or not.
This function closes the ivshmem region
On success it returns 0
On failure it returns -1
*/
int close_ivshmem_region(void)
{
int ret_val = failure;
//Determine if ivshmem region is set up
if (ivshmem_ptr) {
ret_val = munmap(ivshmem_ptr, REGION_SIZE);
ivshmem_ptr = NULL;
}
else
printf("Ivshmem region is not set up.");
return ret_val;
}
/*
size_t read_ivshmem_region(char *ivshmemPtr, char *user_ptr, size_t size)
input: char *user_ptr - The memory to write to
input: size_t size - the number of bytes to read from the memory
output: size_t - The number of bytes that were successfully read or -1 on failure
This function reads from the ivshmem region and copies size - 1 bytes
from the shared memory region to the user_ptr
and null-terminates the string
*/
size_t read_ivshmem_region(char *user_ptr, size_t size)
{
//Number of bytes copied
size_t ret = failure;
//Make sure that we actually need to read something
if (size == 0)
return ret;
//Determine if ivshmem region is set up
if (ivshmem_ptr) {
//Do the copy and zero out the ivshmem region
strncpy(user_ptr, ivshmem_ptr, size - 1);
user_ptr[size] = 0;
ret = strlen(user_ptr);
bzero(ivshmem_ptr, ret);
}
else
printf("Ivshmem region is not set up.");
return ret;
}
/*
size_t write_ivshmem_region(char *ivshmemPtr, char *user_ptr, size_t size)
input: char *user_ptr - The memory to read from
input: int size - the number of bytes to write from the memory
output: int - The number of bytes that were successfully written or -1 on failure
This function reads from the user_ptr and copies size - 1 bytes
to the shared memory region
and null-terminates the string
*/
size_t write_ivshmem_region(char *user_ptr, size_t size)
{
//Return value that holds the amount of bytes that were copied from the user_ptr
size_t ret = failure;
//Make sure that we need to actually write something
if (size == 0)
return ret;
//Determine if ivshmem region is set up
if (ivshmem_ptr) {
//Do the copy and zero out the user_ptr
strncpy(ivshmem_ptr, user_ptr, size - 1);
user_ptr[size] = 0;
ret = strlen(ivshmem_ptr);
bzero(user_ptr, ret);
}
else
printf("Ivshmem region is not set up.");
return ret;
}

View File

@ -0,0 +1,23 @@
/*
* Copyright (C) 2022 Intel Corporation.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/mman.h>
//The size of the shared memory region
#define REGION_SIZE 32768
enum return_vals {success = 0, failure = -1};
int setup_ivshmem_region(const char*);
int close_ivshmem_region(void);
size_t read_ivshmem_region(char *, size_t);
size_t write_ivshmem_region(char *, size_t);

View File

@ -0,0 +1,280 @@
/*
* Copyright (C) 2022 Intel Corporation.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include "userApp.h"
int main(void)
{
//First make sure the user is root
if (geteuid() != 0) {
printf("You need to run this program as root!!!\n");
return failure;
}
//Set up the interprocess communication system between userapp and webapp
shm_addr = setup_ipcomms();
//Open the shared memory region
string pci_fname = "/sys/class/uio/uio0/device/resource2";
if (setup_ivshmem_region(pci_fname.c_str()) == failure) {
perror("Failed to open the shared memory region");
return failure;
}
//Set up signal handler for when we get interrupt
signal(SIGINT, sig_handler);
//Keep track of the latency data
LatencyCounter latencies;
//Loop forever, reading the data and sending it to the UI
while (1) {
//Process a data point
if (process_data(latencies))
latencies.latenciesCount++;
//Dump the data if we have enough data points
if (latencies.latenciesCount % 100 == 0) {
dump_data(latencies, shm_addr);
//Determine if we need to clear the Latency Counter
if (latencies.latenciesCount >= 2000000)
latencies.clear();
}
}
//Close the shared memory region now that we don't need it
close_ivshmem_region();
remove_shm_region(shm_addr);
shm_addr = NULL;
return success;
}
/*
int process_data(LatencyCounter& latencies)
input: LatencyCounter& latencies - The class that holds the current latency data
output: int - 0 on success, -1 on failure
This function processes a data point by reading the ivshmem region, parsing the data,
and incrementing the latency count in the latencies hash table
*/
int process_data(LatencyCounter& latencies)
{
//Clear the data
bzero(data_buffer, BUFFERSIZE);
//Used to hold the latency value
int actual_latency;
//Used to determine if the latency exists in the map
int has_latency;
//Read the data from the RT vm
if (read_ivshmem_region(data_buffer, BUFFERSIZE)) {
actual_latency = failure;
//Deteremine if we have a data point
search_str = strstr(data_buffer, "Act:");
//Scan the data point if we have one
if (search_str)
sscanf(search_str, "Act: %d", &actual_latency);
//Update the latency count
if (actual_latency != failure) {
//Determine if we have the latency value or create it if we do not
has_latency = (*latencies.latencyValues).count(actual_latency);
if (has_latency)
(*latencies.latencyValues)[actual_latency] = (*latencies.latencyValues)[actual_latency] + 1;
else
(*latencies.latencyValues)[actual_latency] = 1;
return 1;
}
}
return success;
}
/*
char *setup_ipcomms(void)
output: char * - A pointer to the shared memory region or NULL on failure
This function sets up the shared memory and synchronization between the userapp and the webapp
*/
char *setup_ipcomms(void)
{
//Set up the shm region
char *shm_region = setup_shm_region();
if (!shm_region)
return shm_region;
//Set up the semaphore for synchronization with initial value of 1
web_sem = sem_open(SEM_KEY, O_CREAT | O_RDWR | O_SYNC, 0666, 1);
if (web_sem == SEM_FAILED) {
perror("Failed to create the semaphore");
remove_shm_region(shm_region);
shm_region = NULL;
}
return shm_region;
}
/*
char *setup_shm_region(void)
output: char * - A pointer to the shared memory region or NULL on failure
This function sets up a shared memory region to be used between the userapp and the
webapp
*/
char *setup_shm_region(void)
{
void *shared_mem_region = NULL;
//ID for the memory region
int shm_id;
shm_unlink(SHM_KEY);
//Set up the shared memory region
shm_id = shm_open(SHM_KEY, O_CREAT | O_RDWR, 0);
if (shm_id == failure) {
perror("Failed to get the shared memory region");
return (char *)shared_mem_region;
}
//Set the size of the shared memory region so we avoid bus error
ftruncate(shm_id, SHM_SIZE);
//Map the shared memory region
shared_mem_region = mmap(0, SHM_SIZE, O_RDWR, MAP_SHARED, shm_id, 0);
if (shared_mem_region == (void *)failure) {
perror("SHMAT ERROR");
shmctl(shm_id, IPC_RMID, NULL);
shared_mem_region = NULL;
return (char *)shared_mem_region;
}
return (char *)shared_mem_region;
}
/*
void remove_shm_region(void)
output: int - 0 on success, -1 on failure
This function tears down the shared memory region
*/
int remove_shm_region(void *shm_region)
{
//Detach the shared memory region from the process
return shmdt(shm_region);
}
/*
int dump_data(LatencyCounter& latencies, char *region)
input: LatencyCounter& latencies - The class that holds the current latency data
input: char *region - The region to copy the data to.
output: int - 0 on success, -1 on failure
This function will dump the latency counts and values to the shared memory region
that the python web server will use
*/
int dump_data(LatencyCounter& latencies, char *region)
{
//Get the shared memory region
char *current_place = region;
if (current_place == (char *)NULL) {
printf("Shared memory region is not setup\n");
return failure;
}
//Holds the total count of the latencies so we can send percentages over
unsigned int total_count = latencies.latenciesCount;
//Latency value percentage
int percentage;
//First lock the semaphore so we can write
sem_wait(web_sem);
//Dump the current count of the values before the percentages
current_place += sprintf(current_place, "%u ", latencies.latenciesCount);
//Iterate over each value in the latencies map, placing the value in the shared memory region
for(unordered_map<int,int>::iterator i = (*latencies.latencyValues).begin(); i != (*latencies.latencyValues).end(); i++) {
percentage = (i->second * 100 / total_count);
//Only copy the latency value if it is at least 1 percentage point to filter out outliers
if (percentage > 0)
current_place += sprintf(current_place, "%d %d ", i->first, percentage);
}
sprintf(current_place, "\n");
sem_post(web_sem);
return success;
}
/*
void sig_handler(int signum)
input: int - the signal value
This function will get run when a signal is sent to the process and will gracefully
shut down the program
*/
void sig_handler(int signum)
{
fprintf(stderr, "Received signal: %d\n", signum);
//Close the shared memory region now that we don't need it
close_ivshmem_region();
remove_shm_region(shm_addr);
shm_addr = NULL;
exit(-1);
}

View File

@ -0,0 +1,90 @@
/*
* Copyright (C) 2022 Intel Corporation.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <iostream>
#include <unordered_map>
#include <string>
#include <semaphore.h>
#include <fcntl.h>
#include <signal.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include "ivshmemlib.h"
using namespace std;
class LatencyCounter
{
public:
//A total count of the latencies
unsigned int latenciesCount;
/*Used for sending the data needed for the histogram to python
First value is the latency data point, second value is the count*/
unordered_map<int,int> *latencyValues;
LatencyCounter()
{
latenciesCount = 0;
latencyValues = new unordered_map<int,int>();
}
~LatencyCounter()
{
latenciesCount = 0;
delete latencyValues;
}
/*
void clear(void)
This method clears the latency counter
*/
void clear(void)
{
latenciesCount = 0;
latencyValues->clear();
}
};
#define SHM_KEY "/pyservershm"
#define SHM_ID 1337
#define SHM_SIZE 1048576
#define SEM_KEY "/pyserversem"
#define BUFFERSIZE 256
//Used for synchronizing between the webserver and the data
sem_t *web_sem;
//Shared memory region between userapp and webapp
char *shm_addr;
//Used for holding the data from cyclictest
char data_buffer[BUFFERSIZE] = {0};
char *search_str;
char *setup_ipcomms(void);
char *setup_shm_region(void);
int remove_shm_region(void *);
int process_data(LatencyCounter&);
int dump_data(LatencyCounter&, char *);
void sig_handler(int);