Commit 76dadb7f authored by Kari Hormi's avatar Kari Hormi

Initial commit

parents
Boottime-EGL/Boottime-EGL.pro.user
DeviceLauncher/DeviceLauncher.pro.user
TEMPLATE = lib
CONFIG += shared c++11
CONFIG -= app_bundle
CONFIG -= qt
TARGET = Boottime_iMX6_EGL
SOURCES += main.cpp
# Default rules for deployment.
qnx: target.path = /usr/lib
else: unix:!android: target.path = /usr/lib
!isEmpty(target.path): INSTALLS += target
HEADERS += \
glextensions.h
#ifndef GLEXTENSIONS_H
#define GLEXTENSIONS_H
#include <EGL/egl.h>
namespace BoottimeEGL
{
bool singleShotted = false;
bool firstBindAPI = false;
}
#endif // GLEXTENSIONS_H
#define _GNU_SOURCE
#include <iostream>
#include <fstream>
#include <dlfcn.h>
#include <string.h>
#include "glextensions.h"
EGLBoolean eglBindAPI(EGLenum api)
{
EGLBoolean (*original_eglBindAPI)(EGLenum api);
original_eglBindAPI = (EGLBoolean (*)(EGLenum)) dlsym(RTLD_NEXT, "eglBindAPI");
EGLBoolean ok = (*original_eglBindAPI)(api);
if (!BoottimeEGL::firstBindAPI && !BoottimeEGL::singleShotted) {
BoottimeEGL::firstBindAPI = true;
BoottimeEGL::singleShotted = true;
}
return ok;
}
EGLBoolean eglSwapBuffers(EGLDisplay dpy, EGLSurface surface)
{
EGLBoolean (*original_eglSwapBuffers)(EGLDisplay, EGLSurface);
original_eglSwapBuffers = (EGLBoolean (*)(EGLDisplay, EGLSurface)) dlsym(RTLD_NEXT, "eglSwapBuffers");
EGLBoolean ok = (*original_eglSwapBuffers)(dpy, surface);
if (BoottimeEGL::firstBindAPI) {
BoottimeEGL::firstBindAPI = false;
std::ofstream exportgpio("/sys/class/gpio/export");
if (exportgpio.fail()) {
std::cerr << "Couldn't open /sys/class/gpio/export" << std::endl;
} else {
exportgpio << 9;
exportgpio.close();
std::ofstream gpiovalue("/sys/class/gpio/gpio9/value");
std::ofstream gpiodirection("/sys/class/gpio/gpio9/direction");
if (gpiovalue.fail() || gpiodirection.fail()) {
std::cerr << "Couldn't open /sys/class/gpio/gpio9/direction or /sys/class/gpio/gpio9/value" << std::endl;
} else {
gpiodirection << "out";
gpiodirection.close();
gpiovalue << "0";
gpiovalue.close();
}
}
}
return ok;
}
TEMPLATE = app
CONFIG += console c++11
CONFIG -= app_bundle
CONFIG -= qt
SOURCES += main.cpp
# Default rules for deployment.
qnx: target.path = /opt/DeviceLauncher/
else: unix:!android: target.path = /usr/DeviceLauncher/
!isEmpty(target.path): INSTALLS += target
#include <iostream>
#include <fstream>
#include <sstream>
#include <chrono>
#include <string>
#include <cmath>
#include <ctime>
#include <poll.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <errno.h>
bool exportGPIO(int gpioNum);
bool unexportGPIO(int gpioNum);
bool mkPath(std::string path);
int main(int argc, char *argv[])
{
std::string savePath;
if (argc > 1) {
std::string argString{argv[1]};
std::size_t foundPos = argString.find("--output-directory=");
if (foundPos != std::string::npos && foundPos == 0) {
std::string pathString = argString.substr(19, argString.size()-19);
if (pathString.size() == 0) {
std::cout << "Empty path specified. Using /var/www/results/" << std::endl;
pathString = "/var/www/results/";
} else if (pathString.at(pathString.size() - 1) != '/') {
pathString.append("/");
}
savePath = pathString;
struct stat st = {0};
if (stat(savePath.c_str(), &st) == -1 && !mkPath(savePath)) {
std::cerr << "Error: could not create directory." << std::endl;
return EXIT_FAILURE;
}
} else {
std::cout << "Invalid flag specified. Usage: DeviceLauncher [--output-directory=OUTPUT_DIR]" << std::endl;
return EXIT_FAILURE;
}
} else {
savePath = "/var/www/results/";
}
std::cout << "Saving results to " << savePath << std::endl;
char buffer[3] = {0,0,0};
double boottimes[10] = {0,0,0,0,0,0,0,0,0,0};
std::chrono::time_point<std::chrono::high_resolution_clock> startPoint, firstFrame;
std::chrono::duration<double, std::milli> boottime;
std::time_t currentTime;
std::time(&currentTime);
const char* date = std::ctime(&currentTime);
double totalSeconds = 0.0;
if (!exportGPIO(4) || !exportGPIO(18)) {
std::cout << "GPIO exporting unsuccessful" << std::endl;
return EXIT_FAILURE;
}
std::ofstream gpio18Direction{"/sys/class/gpio/gpio18/direction"};
gpio18Direction << "out";
gpio18Direction.close();
std::ofstream gpio4Direction{"/sys/class/gpio/gpio4/direction"};
gpio4Direction << "in";
gpio4Direction.close();
std::ofstream gpio4Edge{"/sys/class/gpio/gpio4/edge"};
gpio4Edge << "falling";
gpio4Edge.close();
for (int i = 0; i < 10; ++i) {
std::ofstream gpio18Value{"/sys/class/gpio/gpio18/value"};
gpio18Value << "1";
gpio18Value.close();
startPoint = std::chrono::high_resolution_clock::now();
int fd = open("/sys/class/gpio/gpio4/value", O_RDONLY | O_NONBLOCK);
if (fd < 0) {
perror("open");
return EXIT_FAILURE;
}
struct pollfd fds = {
.fd = fd,
.events = POLLPRI | POLLERR,
};
//Consume prior interrupts
lseek(fds.fd, 0, SEEK_SET);
read(fds.fd, &buffer, 3);
bool deviceOn = false;
usleep(100000);
while (true) {
int ret = poll(&fds, 1, -1);
if (ret >= 1 && (fds.revents & (POLLPRI | POLLERR))) {
lseek(fds.fd, 0, SEEK_SET);
read(fds.fd, &buffer, 3);
char* end;
long bufferValue = strtol(buffer, &end, 10);
//std::cout << bufferValue << std::endl;
if (bufferValue == 0 && deviceOn) {
firstFrame = std::chrono::high_resolution_clock::now();
break;
} else if (bufferValue == 1 && !deviceOn) {
deviceOn = true;
}
}
}
boottime = firstFrame - startPoint;
double boottimeSeconds = ((boottime.count() - 15) / 1000);
std::cout << boottimeSeconds << std::endl;
boottimes[i] = (boottimeSeconds);
totalSeconds += boottimeSeconds;
close(fd);
gpio18Value.open("/sys/class/gpio/gpio18/value");
gpio18Value << "0";
gpio18Value.close();
usleep(5000000);
}
unexportGPIO(4);
unexportGPIO(18);
struct std::tm* timeStruct = std::localtime(&currentTime);
std::ostringstream isoDate;
isoDate << (timeStruct->tm_year + 1900) << '-';
if ((timeStruct->tm_mon + 1) < 10) {
isoDate << 0;
}
isoDate << (timeStruct->tm_mon + 1) << '-';
if ((timeStruct->tm_mday) < 10) {
isoDate << 0;
}
isoDate << timeStruct->tm_mday << 'T';
if ((timeStruct->tm_hour) < 10) {
isoDate << 0;
}
isoDate << timeStruct->tm_hour << ':';
if ((timeStruct->tm_min) < 10) {
isoDate << 0;
}
isoDate << timeStruct->tm_min << ':';
if ((timeStruct->tm_sec) < 10) {
isoDate << 0;
}
isoDate << timeStruct->tm_sec;
std::ostringstream filePathStream;
filePathStream << savePath << "results_" << isoDate.str() << ".html";
std::ofstream outputFile{filePathStream.str()};
outputFile << "<!DOCTYPE html>\n"
"<html>\n"
"<head>\n"
"<title>Results - " << date << "</title>\n"
"<style>\n"
"table, th, td {\n"
" border: 1px solid black;\n"
" border-collapse: collapse;\n"
"}\n"
"th, td {\n"
" padding: 15px;\n"
"}\n"
"</style>\n"
"</head>\n"
"<body>\n"
"<h1>Results - " << date
<< "</h1>\n"
"<table>\n"
"<tr>\n"
"<th>Measurement</th>\n"
"<th>Time</th>\n"
"</tr>\n";
double avgTime = totalSeconds / 10;
double stdTime = 0;
for (int i = 0; i < 10; ++i) {
outputFile << "<tr>\n"
"<td>" << i+1
<< ". boot</td>\n"
"<td>" << boottimes[i]
<< "s</td>\n"
"</tr>\n";
stdTime += std::pow(boottimes[i] - avgTime, 2);
}
stdTime = std::sqrt(stdTime / 10);
outputFile << "<tr>\n"
"<td>Average</td>\n"
"<td>" << avgTime
<< "s</td>\n"
"</tr>\n"
"<tr>\n"
"<td>Standard Deviation</td>\n"
"<td>" << stdTime
<< "s</td>\n"
"</tr>\n"
"</table\n"
"</body>\n"
"</html>";
outputFile.close();
std::cout << "Average time: " << avgTime << std::endl;
std::cout << "Standard Deviation: " << stdTime << std::endl;
return EXIT_SUCCESS;
}
bool exportGPIO(int gpioNum)
{
std::ofstream exportGPIOFile{"/sys/class/gpio/export"};
if (exportGPIOFile.fail()) {
std::cerr << "Failed to open export file. GPIO exporting not possible." << std::endl;
return false;
}
exportGPIOFile << gpioNum;
exportGPIOFile.close();
std::ostringstream gpioDirPath;
gpioDirPath << "/sys/class/gpio/gpio" << gpioNum;
struct stat info;
if(stat(gpioDirPath.str().c_str(), &info) != 0) {
return false;
} else if (info.st_mode & S_IFDIR) {
return true;
}
return false;
}
bool unexportGPIO(int gpioNum)
{
std::ofstream unexportGPIOFile{"/sys/class/gpio/unexport"};
if (unexportGPIOFile.fail()) {
std::cerr << "Failed to open unexport file. GPIO unexporting not possible." << std::endl;
return false;
}
unexportGPIOFile << gpioNum;
unexportGPIOFile.close();
std::ostringstream gpioDirPath;
gpioDirPath << "/sys/class/gpio/gpio" << gpioNum;
struct stat info;
if(stat(gpioDirPath.str().c_str(), &info) != 0) {
return true;
} else if (info.st_mode & S_IFDIR) {
return false;
}
return true;
}
bool mkPath(std::string path)
{
std::cout << "Creating directory to " << path << std::endl;
if (mkdir(path.c_str(), 0755) == -1) {
switch (errno)
{
case ENOENT:
{
int position = path.find_last_of('/');
if (position == path.size()-1) {
position = path.rfind('/', position-1);
}
if (position == std::string::npos || !mkPath(path.substr(0, position))) {
return false;
}
if (mkdir(path.c_str(), 0755) == -1) {
return false;
}
break;
}
case EEXIST:
return true;
default:
return false;
}
}
return true;
}
DeviceLauncher is a program for measuring the boottime of Boot2Qt distribution. The program boots the device and then waits for the signal from it.
This is done ten times with a 5 second cooldown between each measurement. Results are saved /var/www/results or to a directory specified by the user.
DeviceLauncher accesses GPIO ports and requires root access.
Usage: DeviceLauncher [--output-directory=OUTPUT_DIR]
Boottime-EGL is a library for the target device. By default the libraries are deployed to /usr/lib but you can use any directory you wish.
To use the libraries, edit /etc/appcontroller.conf to include line LD_PRELOAD=/usr/lib/libBoottime_iMX6_EGL.so. This preloads the Boottime-libBoottime_iMX6_EGL library that waits for buffer swap and raises a GPIO pin when the first buffer swap happens.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment